public static function getVapidHeaders(string $audience, string $subject, string $publicKey, string $privateKey, string $contentEncoding, ?int $expiration = null)
    {
        $expirationLimit = time() + 43200; // equal margin of error between 0 and 24h
        if (null === $expiration || $expiration > $expirationLimit) {
            $expiration = $expirationLimit;
        }

        $header = [
            'typ' => 'JWT',
            'alg' => 'ES256',
        ];

        $jwtPayload = json_encode([
            'aud' => $audience,
            'exp' => $expiration,
            'sub' => $subject,
        ], JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK);
        if (!$jwtPayload) {
            throw new \ErrorException('Failed to encode JWT payload in JSON');
        }

        list($x, $y) = Utils::unserializePublicKey($publicKey);
        $jwk = JWK::create([
            'kty' => 'EC',
            'crv' => 'P-256',
            'x' => Base64Url::encode($x),
            'y' => Base64Url::encode($y),
            'd' => Base64Url::encode($privateKey),
        ]);

        $jsonConverter = new StandardConverter();
        $jwsCompactSerializer = new CompactSerializer($jsonConverter);
        $jwsBuilder = new JWSBuilder($jsonConverter, AlgorithmManager::create([new ES256()]));
        $jws = $jwsBuilder
            ->create()
            ->withPayload($jwtPayload)
            ->addSignature($jwk, $header)
            ->build();

        $jwt = $jwsCompactSerializer->serialize($jws, 0);
        $encodedPublicKey = Base64Url::encode($publicKey);

        if ($contentEncoding === "aesgcm") {
            return [
                'Authorization' => 'WebPush '.$jwt,
                'Crypto-Key' => 'p256ecdsa='.$encodedPublicKey,
            ];
        } elseif ($contentEncoding === 'aes128gcm') {
            return [
                'Authorization' => 'vapid t='.$jwt.', k='.$encodedPublicKey,
            ];
        }

        throw new \ErrorException('This content encoding is not supported');
    }