HS384 (HMAC-SHA384)

HS384 is a symmetric JWT signing algorithm defined in RFC 7518 that combines HMAC with the SHA-384 hash function. The issuer and every verifier share one secret key, so any service holding that secret can both sign and verify tokens. HS384 sits between HS256 and HS512 in hash output size and recommended secret length. It is less common in mainstream web APIs than HS256, yet it appears in enterprises that mandate SHA-384 minimum strength or migrate from legacy security baselines. When implemented with a 384-bit random secret, explicit algorithm allowlists at verification time, and centralized secret storage, HS384 delivers reliable authentication for monoliths and tightly coupled services where distributing a shared secret is acceptable.

Last updated July 25, 2026

How It Works

HS384 follows the same JWS compact serialization as other HMAC algorithms. Your application builds a JWT header containing {"alg":"HS384","typ":"JWT"} and a payload with claims such as sub, exp, and aud. Both parts are Base64url-encoded without padding, joined by a dot, and that string becomes the HMAC input. The signing step runs HMAC-SHA384 over that input using your shared secret as the key material, producing a 384-bit (48-byte) digest. The digest is Base64url-encoded to form the third segment of the token. Verification repeats the process: decode header and payload, recompute HMAC-SHA384 with the same secret, and compare the result to the signature using a constant-time comparison provided by your crypto library. The JWT size does not grow with the hash width because claims dominate token length; only the signature segment reflects SHA-384 output. Implementation libraries such as jsonwebtoken, PyJWT, and golang-jwt expose HS384 through an explicit algorithm option — never rely on defaults. Pin algorithms: ['HS384'] on verify to block algorithm-confusion attacks where an attacker presents HS256 while your verifier accepts multiple symmetric algorithms with the same secret.

When to Use

Choose HS384 when your security policy or compliance framework requires SHA-384 as the minimum HMAC hash, when you are standardizing on a single symmetric algorithm across an existing fleet that already uses HMAC, or when migrating from systems that explicitly disallow SHA-256 for signing. It fits monoliths, BFF layers, and small microservice groups where one auth service issues tokens and a limited set of backends verify them with access to the same secret via a secrets manager.

When Not to Use

Avoid HS384 when you need third parties or many independent services to verify tokens without access to the signing secret — use RS256, PS256, or ES256 instead. Skip it for greenfield APIs with no SHA-384 mandate; HS256 with a 256-bit secret is simpler and equally practical for most workloads. Do not adopt HS384 if any verifier in your stack lacks library support or if mixed HS256/HS384 deployments would complicate rotation without a coordinated migration plan.

Key Length Requirements

RFC 7518 recommends secret key material at least as long as the hash output. For HS384, generate at least 384 bits (48 bytes) of cryptographically random entropy — 96 hexadecimal characters. Use os.cryptographically secure random APIs (crypto.randomBytes, secrets.token_hex, crypto/rand) rather than passphrases or UUIDs. Store the secret in environment variables or a secrets manager, rotate on compromise or personnel changes, and never reuse an HS256-sized 256-bit secret when upgrading algorithms.

Security Pitfalls

  • Using a secret shorter than 384 bits weakens HMAC strength and violates RFC 7518 guidance for HS384.
  • Accepting multiple symmetric algorithms (e.g., ['HS256','HS384']) with one secret enables algorithm-confusion attacks — pin exactly HS384 at verification.
  • Storing the shared secret in source control, client apps, or logs exposes every verifier to forgery if leaked.
  • Failing to validate exp, aud, and iss claims after signature verification leaves tokens usable outside their intended scope.
  • Reusing the same secret across development, staging, and production allows cross-environment token replay.

Code Examples

Node.js — sign and verify HS384

const crypto = require('crypto');
const jwt = require('jsonwebtoken');

// 384-bit secret (48 bytes = 96 hex chars)
const secret = crypto.randomBytes(48).toString('hex');

const token = jwt.sign(
  { sub: 'user-42', aud: 'api.example.com' },
  secret,
  { algorithm: 'HS384', expiresIn: '15m' }
);

const payload = jwt.verify(token, secret, {
  algorithms: ['HS384'],
  audience: 'api.example.com',
});
console.log(payload.sub);

Python — sign and verify HS384

import secrets
import jwt

secret = secrets.token_hex(48)  # 384-bit key

token = jwt.encode(
    {"sub": "user-42", "aud": "api.example.com"},
    secret,
    algorithm="HS384",
)

payload = jwt.decode(
    token,
    secret,
    algorithms=["HS384"],
    audience="api.example.com",
)
print(payload["sub"])

Go — sign and verify HS384

package main

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"time"

	"github.com/golang-jwt/jwt/v5"
)

func main() {
	key := make([]byte, 48)
	_, _ = rand.Read(key)
	secret := hex.EncodeToString(key)

	token := jwt.NewWithClaims(jwt.SigningMethodHS384, jwt.MapClaims{
		"sub": "user-42",
		"exp": time.Now().Add(15 * time.Minute).Unix(),
	})
	tokenString, _ := token.SignedString([]byte(secret))

	parsed, _ := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {
		if t.Method != jwt.SigningMethodHS384 {
			return nil, fmt.Errorf("unexpected alg: %v", t.Header["alg"])
		}
		return []byte(secret), nil
	})
	fmt.Println(parsed.Valid)
}

Generate Keys

Recommended: 384-bit

Open Jwt Secret Generator

Related Comparisons

Related Terms

Related Guides

Frequently Asked Questions

How do I generate a secret for HS384?

Generate 48 random bytes (384 bits) using a CSPRNG. In Node.js use crypto.randomBytes(48); in Python use secrets.token_hex(48). Store the result as an environment variable or in a secrets manager — never commit it to git.

Should I migrate from HS256 to HS384?

Only if policy requires SHA-384. HS256 with a 256-bit random secret is secure for virtually all web applications. Migration requires re-issuing all tokens, updating every verifier's algorithm allowlist, and rotating to a new 384-bit secret.

Does HS384 make JWT tokens longer?

The signature segment is slightly longer because SHA-384 produces 48 bytes versus 32 for SHA-256, but total token size is dominated by claims. Expect a modest increase of roughly 20 characters in the signature portion.

Can I verify HS384 in middleware without the signing service?

Yes, any service with the shared secret can verify locally. That is the symmetric model's advantage over asymmetric algorithms. Ensure the secret is injected securely and that middleware pins algorithms: ['HS384'] explicitly.

What happens if my secret is only 256 bits?

RFC 7518 expects key material at least as long as the hash output. A 256-bit secret under HS384 reduces effective security below the algorithm's design intent. Generate a fresh 384-bit secret before production deployment.

Looking for a short definition?

Glossary: HS384 (HMAC-SHA384)