Cookie based sessions in Crystal HTTP applications
HEAD Latest release released

Session Build Status

Session is a Crystal's HTTP::Handler that implements cookie based sessions. It can be combined with other bultin or custom handlers, as well as with other Crystal libraries that implement HTTP::Handlers such as kemal.

It takes a lot of inspiration from Rack::Session::Cookie, but it's much smaller, simpler, and obviously less feature-rich. Also less widespread and tested, but you can help with that!

WARNING: this is work in progress and most likely contains security, performance and other kinds of issues I'm working on finding and fixing. I don't use it in production and you shouldn't either.


Add this to your application's shard.yml:

    github: porras/session

Session requires Crystal 0.11.


Session::Handler is a generic class, that is, requires a type to be passed when instantiating it. This type is the data structure where your session data will be stored. This type has to be:

  • Serializable to JSON, either because it's a bultin type that is, or via JSON.mapping if it's a custom type
  • Initializable without parameters

Hash(String, String) makes a sensible yet simple and flexible example. A more strict alternative can be a class whose attributes are nilable so you can define an empty initializer (or provide defaults on it):

class MySession
    time:   {type: String, key: "t", nilable: true},
    visits: {type: Int32, key: "v"},

  def initialize
    @visits = 0

Providing a shorter key helps keeping the cookie size small.

Once you instantiate the handler passing the underlying type and the wanted options (see below), and you put it in the HTTP handlers chain, all downstream handlers will have a context.session available to read and update.


  • secret (mandatory): the content of the session cookie are not encrypted but signed. That is, a user could read the contents (provided that they know the algorithim, which is available in the source code, and pretty simple), but not change it (because the signature wouldn't match). This secret is used for that.
  • session_key (defaults to "cr.session"): name of the cookie where the data will be stored.

Raw HTTP::Handler example

require "http/server"
require "session"

session_handler = Session::Handler(Hash(String, String)).new(secret: "SUPERSECRET")

server ="", "3000", [,,
]) do |context|
  # context.session is a Hash(String, String)
  context.session["first_seen_at"] ||=
  context.response.print "You came first at #{context.session["first_seen_at"]}"


Kemal example

You can easily integrate with Kemal.

require "kemal"
require "session"

session_handler = Session::Handler(Hash(String, String)).new(secret: "SUPERSECRET")
# Add session_handler to Kemal handlers
add_handler session_handler

get "/" do |env|
  env.session["first_seen_at"] ||=
  "You came first at #{env.session["first_seen_at"]}"


  1. Fork it ( )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request


  • porras Sergio Gil - creator, maintainer
  github: porras/session
License MIT
Crystal none


Dependencies 0

Development Dependencies 0

Dependents 0

Last synced .
search fire star recently