Skip to content
ecdsa.com

Sign an ES256 JWT · PHP

How to sign a JWT with ES256 in PHP

To sign a JWT with ES256 in PHP, firebase/php-jwt's JWT::encode takes the claims array, a PEM private key, 'ES256' and an optional kid. It signs through ext-openssl and converts the DER signature into the 64-byte JWS form; the run installs the library with Composer and checks the token with OpenSSL.

Tested with firebase/php-jwt 7.2.1 (PHP 8.5.11, Composer 2.10.3, OpenSSL 3.5.7).

install firebase/php-jwt with Composer
composer require firebase/php-jwt:7.2.1
sign.php — ES256 with firebase/php-jwt
<?php
require __DIR__ . '/vendor/autoload.php';
use Firebase\JWT\JWT;
$privateKey = file_get_contents('key.pem'); // PEM text: PKCS#8 or "EC PRIVATE KEY"
$now = time();
$token = JWT::encode(
[
'iss' => 'https://issuer.example.com',
'sub' => 'user-42',
'aud' => 'api://orders',
'iat' => $now,
'exp' => $now + 15 * 60,
],
$privateKey,
'ES256',
'2026-10', // kid
);
echo $token, PHP_EOL;

How it works

  1. composer require firebase/php-jwt:7.2.1 installs the library; vendor/autoload.php loads it.
  2. The key can be the PEM text (PKCS#8 or EC PRIVATE KEY) or an OpenSSLAsymmetricKey from openssl_pkey_get_private.
  3. The fourth argument of JWT::encode is the kid; a fifth, optional array adds other header fields. typ and alg are set automatically.
  4. The result is the compact token string.

Gotchas

  • php-jwt does not check the curve: a P-384 key with 'ES256' produces a token with a 96-byte signature that no ES256 verifier accepts. Generate P-256 keys for ES256 (prime256v1 in OpenSSL terms).
  • A malformed key throws DomainException: OpenSSL unable to validate key — often a PEM with lost line breaks after passing through an environment variable.
  • php-jwt keeps payload values as you give them: time() returns an integer, but a float or a numeric string would end up in the token in that form. Build iat and exp from integers.
  • Verify the result once with another implementation; signing code that has never been checked by an independent verifier is the usual source of tokens only the issuer itself accepts.

Related recipes