Skip to content

PHP Tutorial PHP Security

Secure Cookies

Lesson 299 of 369 2 min read By Deepak Paswan Updated Oct 5, 2026

Cookies are visible to the user and travel with every request. Three flags and two habits make them much safer.

The three flags

FlagEffectProtects against
SecureSent only over HTTPSReading the cookie on an unencrypted network
HttpOnlyJavaScript cannot read itCookie theft through XSS
SameSiteLimits cross-site sendingCSRF and some tracking
index.php
PHP
<?php
setcookie("theme", "dark", [
    "expires" => time() + 86400 * 30,
    "path" => "/",
    "secure" => true,
    "httponly" => true,
    "samesite" => "Lax",
]);

Which SameSite value?

  • Lax: the cookie is sent on normal navigation to your site (clicking a link), but not on most cross-site sub-requests. A good default.
  • Strict: sent only when the request starts on your own site. Strongest, but a user arriving from an email link may look logged out.
  • None: sent everywhere. Needs Secure. Use only when you really need cross-site cookies.

The __Host- prefix

A cookie name that starts with __Host- is accepted by browsers only if it has Secure, has Path=/ and has no Domain. That stops other subdomains from overwriting it.

index.php
PHP
<?php
setcookie("__Host-session", $value, [
    "path" => "/",
    "secure" => true,
    "httponly" => true,
    "samesite" => "Lax",
]);

A helper

index.php
PHP
<?php
function cookieOptions(int $days, bool $https): array
{
    return [
        "expires" => time() + $days * 86400,
        "path" => "/",
        "secure" => $https,
        "httponly" => true,
        "samesite" => "Lax",
    ];
}

$o = cookieOptions(7, true);

echo ($o["secure"] ? "Secure " : "") . ($o["httponly"] ? "HttpOnly " : "") . "SameSite=" . $o["samesite"];
Output
OUTPUT
Secure HttpOnly SameSite=Lax
  • A trusted value: roles, prices, “logged_in=1”. The user can edit it.
  • A place for secrets such as passwords.
  • Large. It is sent on every request.

If a value matters, store it on the server (session or database) and keep only a random ID in the cookie. A “remember me” token is a random value checked against a hashed copy in the database (see Remember Me).

Sign values if you must store data

If you need a cookie to carry data that the user must not alter, sign it with a secret key, and verify on arrival.

index.php
PHP
<?php
function signCookie(string $value, string $key): string
{
    return $value . "." . hash_hmac("sha256", $value, $key);
}

function readSignedCookie(string $cookie, string $key): ?string
{
    $pos = strrpos($cookie, ".");

    if ($pos === false) {
        return null;
    }

    $value = substr($cookie, 0, $pos);

    return hash_equals(hash_hmac("sha256", $value, $key), substr($cookie, $pos + 1)) ? $value : null;
}

$key = "a-long-random-secret-from-the-environment";

$cookie = signCookie("lang=hi", $key);

var_dump(readSignedCookie($cookie, $key));
var_dump(readSignedCookie("lang=en." . explode(".", $cookie)[1], $key));   // tampered value
Output
OUTPUT
string(7) "lang=hi"
NULL

The signature proves that you created the value and nobody changed it. It does not hide it: the user can still read the value. Use encryption (sodium_crypto_secretbox) if the content must also be private.

  • Set path as narrow as makes sense.
  • Do not set domain unless you need to share the cookie with subdomains. Setting domain=example.com shares it with all subdomains, including ones run by someone else.

Delete cookies with the same attributes

To remove a cookie, send it again with an expiry in the past and the same path and domain. See Deleting Cookies.

dangerous
HTTPS first
The Secure flag only helps if the site really uses HTTPS everywhere. Set it up, redirect HTTP to HTTPS and consider the HSTS header (see Security Headers).