# Sessions and Cookies in PHP

> How PHP sessions and cookies work: session ID, storage, php.ini defaults, upload progress and the PHP2Shell interim measure.

Source: https://phpsos.de/en/php/sessions-und-cookies/  
As of: 25/10/2026  
Provider: PHP SOS, PHP emergency help and server security. Immediate help via WhatsApp: https://wa.me/491736494914?text=Hello%20PHP%20SOS%2C%20we%20need%20help%20with%20our%20PHP%20server.%20Company%3A%20%E2%80%A6%20Website%3A%20%E2%80%A6

**In short:** HTTP remembers nothing between two requests. A PHP session solves this: PHP assigns a random session ID, sends it to the browser as a cookie and stores the associated data on the server, by default as a file. On every further request, `session_start()` reads the data back using the ID. The PHP session also includes the function `session.upload_progress`, which php.net recommends temporarily switching off on the server side because of PHP2Shell.

## Why web applications need sessions

Every request to a web server stands on its own. Without an additional mechanism, a shop would no longer know, when you click "shopping cart", who just put something into it, and an admin area would have to ask for the password on every click. Sessions join individual requests into one session.

For decision-makers this means: after login, the session ID is worth as much as username and password combined. For admins it means: where and how session data is stored is a security question, especially on servers with several websites.

## What happens at session_start()

The process is the same on every request and can be described in five steps:

1. The browser sends along the cookie with the session name, by default `PHPSESSID`.
2. `session_start()` reads the ID and passes it to the storage handler, by default `files`.
3. The handler loads the stored data, PHP converts it back with the `serialize_handler` and fills `$_SESSION`.
4. The script reads and changes `$_SESSION` like a normal array.
5. At the end of the request, PHP writes the data back. With `session.lazy_write` (default: on), only if it has changed.

```php
<?php
session_start();

$_SESSION['besuche'] = ($_SESSION['besuche'] ?? 0) + 1;
echo 'Seitenaufrufe in dieser Sitzung: ', (int) $_SESSION['besuche'];
```

If there is no session yet, PHP generates a new ID and sets the cookie. If `session.use_strict_mode` is active, PHP rejects unknown IDs supplied by the browser and generates a new one. The PHP manual describes this setting as mandatory for basic security.

## Defaults worth knowing

According to the PHP manual, the following values apply when nothing else is configured. Many distributions and hosting providers set their own values, so the configuration that is actually effective always counts.

| Setting | Default | Meaning |
|---|---|---|
| `session.save_handler` | `files` | storage location of the data, Redis or database also possible |
| `session.name` | `PHPSESSID` | name of the cookie |
| `session.serialize_handler` | `php` | format of the stored data |
| `session.use_strict_mode` | `0` | unknown IDs are accepted |
| `session.use_only_cookies` | `1` | no session ID in the address |
| `session.cookie_httponly` | `0` | JavaScript can read the cookie |
| `session.cookie_secure` | `0` | cookie also over unencrypted HTTP |
| `session.cookie_samesite` | empty | no SameSite attribute |
| `session.gc_maxlifetime` | `1440` | seconds until possible clean-up |
| `session.gc_probability` / `gc_divisor` | `1` / `100` | clean-up at roughly every hundredth start |
| `session.upload_progress.enabled` | `1` | upload progress is written into the session |

The values for `cookie_httponly`, `cookie_secure` and `use_strict_mode` are therefore not designed for security out of the box. How to set them correctly is described in the guide [Securing sessions in PHP](https://phpsos.de/en/ratgeber/session-sicherheit-php/). The settings `session.sid_length` and `session.sid_bits_per_character` have been marked as deprecated since PHP 8.4.0.

## Setting cookies directly

Besides the session cookie, an application can set its own cookies. Since PHP 7.3.0, `setcookie()` accepts an array of options for this, including `samesite`. Since PHP 8.5.0 there is additionally the option `partitioned`.

```php
<?php
setcookie('sprache', 'de', [
    'expires'  => time() + 60 * 60 * 24 * 30,
    'path'     => '/',
    'secure'   => true,
    'httponly' => true,
    'samesite' => 'Lax',
]);
```

According to the manual, if `samesite` equals `None`, `secure` must also be set, otherwise the browser blocks the cookie. Cookies are data from the browser: their content comes back on the next request via `$_COOKIE` and must be checked like any other input. More on this on the page [Superglobals](https://phpsos.de/en/php/superglobals/).

## session.upload_progress: progress display for uploads

During a file upload, PHP can write the progress into the session. For this, the form sends a hidden field with the name from `session.upload_progress.name`, by default `PHP_SESSION_UPLOAD_PROGRESS`. PHP then stores the data in `$_SESSION` under a key with the prefix `upload_progress_`, with details such as start time, total size and bytes already processed.

Important for understanding: according to the manual, PHP writes this information into the session before any script is executed. The function therefore works while the request is being processed, before the application code. Its settings are of the type `INI_PERDIR` and cannot be changed at runtime.

## Sessions and PHP2Shell: the interim measure

One of the interim measures against the PHP2Shell flaws named by php.net is to set `session.upload_progress.enabled=0` in php.ini, in the FPM pool or in the server configuration and then restart all workers, explicitly not via `ini_set()` or `.user.ini`. On 23 October 2026 the notice on [CVE-2026-115560](https://phpsos.de/en/php2shell/cve-2026-115560/) was added, with the same remedy until the next regular bugfix release.

```ini
; FPM pool of the website, then restart all workers
php_admin_flag[session.upload_progress.enabled] = off
```

According to trade reports, this setting alone does not close both flaws. The update to PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35 remains mandatory. Whether your application uses upload progress at all and how to set the setting permanently is described in the article [Switching off session.upload_progress](https://phpsos.de/en/blog/session-upload-progress-abschalten/).

## How does PHP SOS help?

- We read out the session settings that are actually effective for each website, not just the values from the command line.
- We set the PHP2Shell interim measures per pool, restart all workers and check the result.
- We separate session storage locations and pool users between websites as part of [server hardening](https://phpsos.de/en/leistungen/server-haertung/).
- After an incident we invalidate all existing sessions so that stolen session IDs become worthless.


## Frequently asked questions

**What is the difference between a cookie and a session?**  
A cookie is a small data record that the browser stores and sends along with every request. A session stores the actual data on the server; the cookie contains only a random identifier, the session ID.

**How long does a PHP session live?**  
By default the session cookie is valid until the browser is closed. The data on the server may be cleaned up after session.gc_maxlifetime, by default 1440 seconds without access. When that actually happens depends on the system's clean-up logic.

**What does session.upload_progress do?**  
During a file upload, the function writes the progress into the session so that a second request can display it. It is active by default. Because of PHP2Shell, php.net recommends temporarily switching it off on the server side.

**Is it enough to switch off session.upload_progress with ini_set()?**  
No. According to the manual, the setting cannot be changed at runtime anyway, and php.net explicitly names php.ini, the FPM pool or the server configuration as the right places, not ini_set() or .user.ini. Then restart all workers.

## Sources

- [PHP Manual: Session Configuration](https://www.php.net/manual/de/session.configuration.php)
- [PHP Manual: Session Upload Progress](https://www.php.net/manual/de/session.upload-progress.php)
- [PHP Manual: setcookie](https://www.php.net/manual/en/function.setcookie.php)
- [php.net: Security Announcements](https://www.php.net/)
