PHPackages                             ocramius/psr7-session - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. ocramius/psr7-session

Abandoned → [psr7-sessions/storageless](/?search=psr7-sessions%2Fstorageless)Library

ocramius/psr7-session
=====================

Storageless PSR-7 Session support

10.1.0(6mo ago)6503.5k38[3 issues](https://github.com/psr7-sessions/storageless/issues)MITPHPPHP ~8.4.0 || ~8.5.0CI passing

Since Nov 19Pushed 1mo ago19 watchersCompare

[ Source](https://github.com/psr7-sessions/storageless)[ Packagist](https://packagist.org/packages/ocramius/psr7-session)[ RSS](/packages/ocramius-psr7-session/feed)WikiDiscussions 10.2.x Synced 1mo ago

READMEChangelog (10)Dependencies (14)Versions (81)Used By (0)

PSR-7 Storage-less HTTP Sessions
================================

[](#psr-7-storage-less-http-sessions)

[![Mutation testing badge](https://camo.githubusercontent.com/0efcf35de585a97ad7d3937e452e2b0492542e99bd9b4a668821678faf896d3b/68747470733a2f2f696d672e736869656c64732e696f2f656e64706f696e743f7374796c653d666c61742675726c3d687474707325334125324625324662616467652d6170692e737472796b65722d6d757461746f722e696f2532466769746875622e636f6d253246707372372d73657373696f6e7325324673746f726167656c657373253246392e322e78)](https://dashboard.stryker-mutator.io/reports/github.com/psr7-sessions/storageless/9.2.x)[![Type Coverage](https://camo.githubusercontent.com/ffea4747d144b76bae1315e17169e6d0f8dfaa5b3a2a892b05d31f1585e761d5/68747470733a2f2f73686570686572642e6465762f6769746875622f707372372d73657373696f6e732f73746f726167656c6573732f636f7665726167652e737667)](https://shepherd.dev/github/psr7-sessions/storageless)[![Packagist](https://camo.githubusercontent.com/00bb53be0814a1a67e3bcfffa3dbab6d36d2a0a75d06c36b07cb68ac465d11ba/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f707372372d73657373696f6e732f73746f726167656c6573732e737667)](https://packagist.org/packages/psr7-sessions/storageless)[![Packagist](https://camo.githubusercontent.com/3c02c88164d10af78c2eea371e87ba1e4f30726ed4c8a67b91bd362cedbc7eaa/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f767072652f707372372d73657373696f6e732f73746f726167656c6573732e737667)](https://packagist.org/packages/psr7-sessions/storageless)

**PSR7Session** is a [PSR-7](https://www.php-fig.org/psr/psr-7/) and [PSR-15](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-15-request-handlers.md)compatible [middleware](https://mwop.net/blog/2015-01-08-on-http-middleware-and-psr-7.html) that enables session without I/O usage in PSR-7 based applications.

Proudly brought to you by [ocramius](https://github.com/Ocramius), [malukenho](https://github.com/malukenho) and [lcobucci](https://github.com/lcobucci).

Installation
------------

[](#installation)

```
composer require psr7-sessions/storageless
```

Usage
-----

[](#usage)

You can use the `PSR7Sessions\Storageless\Http\SessionMiddleware` in any [PSR-15](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-15-request-handlers.md)compatible middleware.

In a [`mezzio/mezzio`](https://github.com/mezzio/mezzio)application, this would look like following:

```
use Lcobucci\JWT\Configuration as JwtConfig;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key\InMemory;
use PSR7Sessions\Storageless\Http\SessionMiddleware;
use PSR7Sessions\Storageless\Http\Configuration as StoragelessConfig;

$app = new \Mezzio\Application(/* ... */);

$app->pipe(new SessionMiddleware(
    StoragelessConfig::fromJwtConfiguration(
        JwtConfig::forSymmetricSigner(
            new Signer\Hmac\Sha256(),
            InMemory::base64Encoded('OpcMuKmoxkhzW0Y1iESpjWwL/D3UBdDauJOe742BJ5Q='), // replace this with a key of your own (see below)
        )
    )
));
```

After this, you can access the session data inside any middleware that has access to the `Psr\Http\Message\ServerRequestInterface` attributes:

```
$app->get('/get', function (ServerRequestInterface $request, ResponseInterface $response) : ResponseInterface {
    $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
    assert($session instanceof \PSR7Sessions\Storageless\Session\SessionInterface);
    $session->set('counter', $session->get('counter', 0) + 1);

    $response
        ->getBody()
        ->write('Counter Value: ' . $session->get('counter'));

    return $response;
});
```

You can do this also in asynchronous contexts and long-running processes, since no super-globals nor I/O are involved.

It is recommended that you use a key with lots of entropy, preferably generated using a cryptographically secure pseudo-random number generator (CSPRNG). You can use the [CryptoKey tool](https://github.com/AndrewCarterUK/CryptoKey)to do this for you.

Note that you can also use asymmetric keys; please refer to [`lcobucci/jwt`](https://packagist.org/packages/lcobucci/jwt) documentation:

1. The `Configuration` object:
2. Supported algorithms:

### Session Hijacking mitigation

[](#session-hijacking-mitigation)

To mitigate the risks associated to cookie stealing and thus [session hijacking](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html#binding-the-session-id-to-other-user-properties), you can bind the user session to its IP (`$_SERVER['REMOTE_ADDR']`) and User-Agent (`$_SERVER['HTTP_USER_AGENT']`) by enabling client fingerprinting:

```
use PSR7Sessions\Storageless\Http\SessionMiddleware;
use PSR7Sessions\Storageless\Http\Configuration as StoragelessConfig;
use PSR7Sessions\Storageless\Http\ClientFingerprint\Configuration as FingerprintConfig;

$app = new \Mezzio\Application(/* ... */);

$app->pipe(new SessionMiddleware(
    StoragelessConfig::fromJwtConfiguration(/* ... */)
        ->withClientFingerprintConfiguration(
            FingerprintConfig::forIpAndUserAgent()
        )
));
```

If your PHP service is behind a reverse proxy of yours, [you may need to retrieve the client IP from a different source of truth](https://adam-p.ca/blog/2022/03/x-forwarded-for/). In such cases you can extract the information you need by writing a custom `\PSR7Sessions\Storageless\Http\ClientFingerprint\Source` implementation:

```
use Psr\Http\Message\ServerRequestInterface;
use PSR7Sessions\Storageless\Http\SessionMiddleware;
use PSR7Sessions\Storageless\Http\Configuration as StoragelessConfig;
use PSR7Sessions\Storageless\Http\ClientFingerprint\Configuration as FingerprintConfig;
use PSR7Sessions\Storageless\Http\ClientFingerprint\Source;

$app = new \Mezzio\Application(/* ... */);

$app->pipe(new SessionMiddleware(
    StoragelessConfig::fromJwtConfiguration(/* ... */)
        ->withClientFingerprintConfiguration(
            FingerprintConfig::forSources(new class implements Source{
                 public function extractFrom(ServerRequestInterface $request): string
                 {
                     return $request->getHeaderLine('X-Real-IP');
                 }
            })
        )
));
```

### Examples

[](#examples)

Simply browse to the `examples` directory in your console, then run

```
php -S localhost:9999 index.php
```

Then try accessing `http://localhost:9999`: you should see a counter that increases at every page refresh

WHY?
----

[](#why)

In most PHP+HTTP related projects, `ext/session` serves its purpose and allows us to store server-side information by associating a certain identifier to a visiting user-agent.

What is the problem with `ext/session`?
---------------------------------------

[](#what-is-the-problem-with-extsession)

This is all fair and nice, except for:

- relying on the `$_SESSION` superglobal
- relying on the shutdown handlers in order to "commit" sessions to the storage
- having a huge limitation of number of active users (due to storage)
- having a lot of I/O due to storage
- having serialized data cross different processes (PHP serializes and de-serializes `$_SESSION` for you, and there are security implications)
- having to use a centralized storage for setups that scale horizontally
- having to use sticky sessions (with a "smart" load-balancer) when the storage is not centralized
- not designed to be used for multiple dispatch cycles

What does this project do?
--------------------------

[](#what-does-this-project-do)

This project tries to implement storage-less sessions and to mitigate the issues listed above.

Assumptions
-----------

[](#assumptions)

- your sessions are fairly small and contain only few identifiers and some CSRF tokens. Small means `< 400` bytes
- data in your session is `JsonSerializable` or equivalent
- data in your session is **freely readable by the client**

How does it work?
-----------------

[](#how-does-it-work)

Session data is directly stored inside a session cookie as a JWT token.

This approach is not new, and is commonly used with `Bearer` tokens in HTTP/REST/OAuth APIs.

In order to guarantee that the session data is not modified, that the client can trust the information and that the expiration date is mutually agreed between server and client, a [JWT token](https://tools.ietf.org/html/rfc7519)is used to transmit the information.

The JWT token is always signed to ensure that the user-agent is never able to manipulate the session. Both symmetric and asymmetric keys are supported for signing/verifying tokens.

Advantages
----------

[](#advantages)

- no storage required
- no sticky sessions required (any server having a copy of the private or public keys can generate sessions or consume them)
- can transmit cleartext information to the client, allowing it to share some information with the server (a standard example is about sharing the "username" or "user-id" in a given session)
- can transmit encrypted information to the client, allowing server-only consumption of the information
- not affected by PHP serialization RCE attacks
- not limited to PHP process scope: can have many sessions per process
- no reliance on global state
- when in a multi-server setup, you may allow read-only access to servers that only have access to public keys, while writes are limited to servers that have access to private keys
- can be used over multiple dispatch cycles

Configuration options
---------------------

[](#configuration-options)

Please refer to the [configuration documentation](docs/configuration.md).

Known limitations
-----------------

[](#known-limitations)

Please refer to the [limitations documentation](docs/limitations.md).

Contributing
------------

[](#contributing)

Please refer to the [contributing notes](CONTRIBUTING.md).

License
-------

[](#license)

This project is made public under the [MIT LICENSE](LICENSE).

###  Health Score

65

—

FairBetter than 99% of packages

Maintenance81

Actively maintained with recent releases

Popularity39

Limited adoption so far

Community28

Small or concentrated contributor base

Maturity96

Battle-tested with a long release history

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~49 days

Recently: every ~94 days

Total

77

Last Release

61d ago

Major Versions

5.1.0 → 6.0.x-dev2020-03-02

6.0.0 → 7.0.02020-04-10

7.4.x-dev → 8.0.02021-02-01

8.18.x-dev → 9.0.02023-07-25

9.3.x-dev → 10.0.02025-02-26

PHP version history (12 changes)1.0.0PHP ~7.0

4.0.0PHP ^7.1

5.0.x-devPHP ^7.2

6.0.x-devPHP ^7.4

7.1.x-devPHP ^7.4.7

8.0.0PHP ^7.4.7 || ~8.0.0

8.1.0PHP ~8.0.0 || ~8.1.0

8.11.x-devPHP ~8.0.0 || ~8.1.0 || ~8.2.0

8.12.x-devPHP ~8.1.0 || ~8.2.0

9.1.x-devPHP ~8.2.0 || ~8.3.0

9.2.x-devPHP ~8.3.0 || ~8.4.0

10.1.0PHP ~8.4.0 || ~8.5.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/fd0445bc21fa116c259b5889377b90cbd8a34d49357321f76a74f6d2c2ae6b0c?d=identicon)[Ocramius](/maintainers/Ocramius)

---

Top Contributors

[![renovate[bot]](https://avatars.githubusercontent.com/in/2740?v=4)](https://github.com/renovate[bot] "renovate[bot] (646 commits)")[![Ocramius](https://avatars.githubusercontent.com/u/154256?v=4)](https://github.com/Ocramius "Ocramius (444 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (336 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (327 commits)")[![Slamdunk](https://avatars.githubusercontent.com/u/152236?v=4)](https://github.com/Slamdunk "Slamdunk (142 commits)")[![dependabot-preview[bot]](https://avatars.githubusercontent.com/in/2141?v=4)](https://github.com/dependabot-preview[bot] "dependabot-preview[bot] (76 commits)")[![lcobucci](https://avatars.githubusercontent.com/u/201963?v=4)](https://github.com/lcobucci "lcobucci (23 commits)")[![AndrewCarterUK](https://avatars.githubusercontent.com/u/6486835?v=4)](https://github.com/AndrewCarterUK "AndrewCarterUK (10 commits)")[![martinezdelariva](https://avatars.githubusercontent.com/u/1567749?v=4)](https://github.com/martinezdelariva "martinezdelariva (6 commits)")[![samsonasik](https://avatars.githubusercontent.com/u/459648?v=4)](https://github.com/samsonasik "samsonasik (3 commits)")[![mnapoli](https://avatars.githubusercontent.com/u/720328?v=4)](https://github.com/mnapoli "mnapoli (3 commits)")[![drupol](https://avatars.githubusercontent.com/u/252042?v=4)](https://github.com/drupol "drupol (2 commits)")[![danizord](https://avatars.githubusercontent.com/u/1850941?v=4)](https://github.com/danizord "danizord (2 commits)")[![holtkamp](https://avatars.githubusercontent.com/u/776405?v=4)](https://github.com/holtkamp "holtkamp (1 commits)")[![stof](https://avatars.githubusercontent.com/u/439401?v=4)](https://github.com/stof "stof (1 commits)")[![hannesvdvreken](https://avatars.githubusercontent.com/u/1410358?v=4)](https://github.com/hannesvdvreken "hannesvdvreken (1 commits)")[![lferro9000](https://avatars.githubusercontent.com/u/2652260?v=4)](https://github.com/lferro9000 "lferro9000 (1 commits)")[![maltsev](https://avatars.githubusercontent.com/u/923973?v=4)](https://github.com/maltsev "maltsev (1 commits)")[![basz](https://avatars.githubusercontent.com/u/143068?v=4)](https://github.com/basz "basz (1 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/ocramius-psr7-session/health.svg)

```
[![Health](https://phpackages.com/badges/ocramius-psr7-session/health.svg)](https://phpackages.com/packages/ocramius-psr7-session)
```

###  Alternatives

[cakephp/cakephp

The CakePHP framework

8.8k18.5M1.6k](/packages/cakephp-cakephp)[flarum/core

Delightfully simple forum software.

211.3M1.9k](/packages/flarum-core)[neos/flow

Flow Application Framework

862.0M450](/packages/neos-flow)[neos/flow-development-collection

Flow packages in a joined repository for pull requests.

144179.3k3](/packages/neos-flow-development-collection)[windwalker/framework

The next generation PHP framework.

25639.1k1](/packages/windwalker-framework)[selective/samesite-cookie

Secure your site with SameSite cookies

10144.0k](/packages/selective-samesite-cookie)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
