Menu

Sessões e cookies em PHP: session_start e setcookie

Chame session_start() no topo de cada página e guarde valores em $_SESSION['key']; o PHP os mantém no servidor e reconhece o visitante por um cookie. setcookie('name', 'value', $options) guarda um valor pequeno no navegador, lido de volta em $_COOKIE. Veja login, logout, mensagens flash, opções de cookie e as configurações de segurança.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Chame session_start() no topo de uma página e depois leia e escreva $_SESSION['key'] como qualquer array: o PHP o salva no servidor e o encontra de novo na próxima requisição do visitante. Para um valor pequeno que o navegador deve guardar, chame setcookie('name', 'value', time() + 86400) e leia de volta nas requisições seguintes em $_COOKIE['name'].

<?php
session_start();                        // before any output
setcookie('theme', 'dark', time() + 60 * 60 * 24 * 30);   // 30 days, also before output

$_SESSION['visits'] = ($_SESSION['visits'] ?? 0) + 1;
$theme = $_COOKIE['theme'] ?? 'light';  // set on an earlier visit
echo "You have opened this page {$_SESSION['visits']} times.";

Sessões e cookies só fazem sentido ao longo de várias requisições HTTP de um navegador, então os exemplos que precisam de um são código simples. A lógica em volta deles é PHP comum com arrays, e essa parte roda aqui: em cada bloco executável, $session faz o papel de $_SESSION, e cada chamada é uma requisição.

Como uma sessão PHP funciona

  1. O primeiro session_start() cria um id aleatório, o envia ao navegador num cookie chamado PHPSESSID e dá a você um $_SESSION vazio.
  2. No fim da requisição, o PHP salva o $_SESSION no servidor (em arquivos por padrão, ou no Redis, ou num banco de dados).
  3. Na próxima requisição, o navegador envia o cookie de volta, o session_start() lê o id e carrega o array salvo.

O navegador nunca vê os dados, só o id. É por isso que o id do usuário logado vai no $_SESSION e nunca num cookie seu: um visitante pode editar os cookies dele para qualquer valor, mas não pode editar o $_SESSION.

Fazer login e logout de um usuário com sessão

Fazer login é: verificar a senha, gerar um novo id de sessão, guardar o id do usuário. Fazer logout é o inverso, e tem três passos.

<?php
// login.php
session_start();

$user = find_user_by_email($_POST['email'] ?? '');
if ($user && password_verify($_POST['password'] ?? '', $user['password_hash'])) {
    session_regenerate_id(true);        // new id after login: stops session fixation
    $_SESSION['user_id'] = $user['id'];
    header('Location: /dashboard.php');
    exit;
}
$error = 'Wrong email or password.';
<?php
// any protected page
session_start();
if (!isset($_SESSION['user_id'])) {
    header('Location: /login.php');
    exit;
}
<?php
// logout.php
session_start();
$_SESSION = [];                                     // 1. forget the data

$p = session_get_cookie_params();                   // 2. delete the cookie
setcookie(session_name(), '', time() - 3600, $p['path'], $p['domain'], $p['secure'], $p['httponly']);

session_destroy();                                  // 3. delete the stored session
header('Location: /');
exit;

O session_regenerate_id(true) importa mais do que parece. Se um atacante consegue fazer uma vítima usar um id de sessão que o atacante conhece (por exemplo, por um link), e o id continua o mesmo depois do login, o atacante também fica logado. Um id novo no login fecha essa brecha. A verificação da senha em si é explicada na página sobre password_hash.

Expirar uma sessão por inatividade

A limpeza do próprio PHP (session.gc_maxlifetime, 1440 segundos por padrão) roda de forma aleatória e não é um tempo limite confiável. Guarde a hora da última requisição e verifique você mesmo. O bloco usa timestamps fixos para você ver cada caso; mude $timeout ou os horários.

O intervalo de 09:05 a 09:16:40 é de 700 segundos, abaixo do limite de 15 minutos, então o usuário continua logado. O intervalo seguinte é de 1000 segundos, então a sessão é limpa.

Mensagens flash

Uma mensagem flash é definida numa requisição e mostrada uma vez na próxima, tipicamente "Salvo." depois de um redirecionamento. Guarde-a na sessão e depois leia e remova num passo:

O setcookie() recebe o nome, o valor e um array de opções (PHP 7.3 e posteriores). Defina estas quatro opções em todo cookie, a menos que tenha um motivo para não fazer:

<?php
setcookie('theme', 'dark', [
    'expires'  => time() + 60 * 60 * 24 * 30,   // 30 days; 0 means "until the browser closes"
    'path'     => '/',                          // send it on every page, not just this folder
    'secure'   => true,                         // only over HTTPS
    'httponly' => true,                         // JavaScript cannot read it
    'samesite' => 'Lax',                        // not sent on most cross-site requests
]);

$theme = $_COOKIE['theme'] ?? 'light';

Duas coisas confundem:

  • O $_COOKIE não muda na mesma requisição. Ele guarda o que o navegador enviou com esta requisição. Um cookie que você define agora aparece no $_COOKIE na próxima.
  • Cookies são cabeçalhos, então o setcookie() precisa rodar antes de qualquer saída, exatamente como session_start() e header().

Para apagar um cookie, defina-o de novo com o mesmo nome e path e uma expiração no passado: setcookie('theme', '', ['expires' => time() - 3600, 'path' => '/']).

O valor de um cookie é uma string de no máximo uns 4 KB, então guarde dados estruturados como JSON, e trate o que volta como não confiável: o visitante pode alterá-lo. Nunca use unserialize() num cookie: ele pode criar objetos das suas próprias classes e executar os métodos mágicos delas, enquanto o json_decode() só retorna arrays, strings, números, booleanos, null ou stdClass.

A página sobre json_encode explica as flags. Para qualquer coisa maior que alguns valores curtos, mantenha os dados na sessão (ou num banco de dados) e guarde só uma chave no cookie.

Tokens de "lembrar de mim"

Um cookie de "lembrar de mim" não pode guardar o id do usuário nem o hash da senha. Guarde um token aleatório longo no cookie e só o hash dele no banco de dados, e compare com hash_equals():

Os valores mudam a cada execução, porque o random_bytes() é aleatório. Se o banco de dados vazar, os hashes são inúteis sem os cookies, e o hash_equals() compara em tempo constante, para que a verificação não vaze informação pelo tempo.

Sessão ($_SESSION)Cookie ($_COOKIE)
GuardadoNo servidorNo navegador
O visitante pode ler ou editarNão (só o id)Sim
TamanhoLimitado pelo seu armazenamentoUns 4 KB por cookie
DuraAté o navegador fechar ou a sessão expirarAté o horário do expires
Use paraUsuário logado, carrinho, token CSRF, mensagens flashTema, idioma, escolha de consentimento, token de "lembrar de mim"

Erro comum: headers already sent

session_start(), setcookie() e header() enviam cabeçalhos HTTP, e cabeçalhos só podem ser enviados antes do corpo. Qualquer saída antes, até uma linha em branco antes do <?php num arquivo incluído, produz:

Warning: session_start(): Session cannot be started after headers have already been sent in /var/www/page.php on line 3
Warning: Cannot modify header information - headers already sent by (output started at /var/www/page.php:2) in /var/www/page.php on line 4

A segunda mensagem diz onde a saída começou (page.php:2): vá até essa linha e remova a saída, ou mova o session_start() para cima dela. Em arquivos que contêm só PHP, omitir o ?> final evita que espaços perdidos depois dele virem saída.

Perguntas frequentes

Qual a diferença entre sessão e cookie no PHP?

Um cookie fica guardado no navegador do visitante e é enviado em cada requisição, então o visitante pode lê-lo e alterá-lo. Uma sessão guarda os dados no servidor; o navegador só guarda um id de sessão aleatório num cookie (PHPSESSID). Coloque tudo o que precisa ser confiável, como o id do usuário logado, em $_SESSION.

Como destruo uma sessão no logout no PHP?

Esvazie os dados, apague o cookie da sessão e destrua a sessão guardada: $_SESSION = [];, depois setcookie(session_name(), '', time() - 3600, '/'), depois session_destroy();. Redirecione em seguida para que a próxima página comece do zero.

Como apago um cookie no PHP?

Defina-o de novo com o mesmo nome, caminho e domínio e uma expiração no passado: setcookie('theme', '', ['expires' => time() - 3600, 'path' => '/']);. Também faça unset($_COOKIE['theme']) se o resto do script atual o lê, porque o $_COOKIE só muda na próxima requisição.

Quanto tempo dura uma sessão PHP?

Por padrão, o cookie da sessão dura até o navegador fechar (session.cookie_lifetime = 0) e o servidor pode apagar os dados da sessão depois de 1440 segundos (24 minutos) sem atividade (session.gc_maxlifetime). Para um tempo limite fixo, guarde a hora da última requisição na sessão e verifique você mesmo.

Por que o session_start() diz headers already sent?

O id da sessão viaja num cabeçalho de cookie, e os cabeçalhos precisam ser enviados antes de qualquer saída. Um echo, HTML, ou até uma linha em branco ou um BOM antes do <?php envia a saída primeiro. Chame session_start() bem no topo do script, antes de qualquer coisa ser impressa.

Ilustração das linguagens de programação do Coddy

Aprenda a programar com o Coddy

COMEÇAR