The exp claim in a JSON Web Token (JWT) defines the token's expiration time as a Unix timestamp. After this time, the token must be rejected by any compliant server. If you work with authentication systems and need to quickly check whether a token has expired, the JWT Expiry Checker lets you decode and verify expiration entirely in your browser — no token data is ever uploaded.
How the exp Claim Works
The exp claim is defined in RFC 7519 Section 4.1.4 as a NumericDate value — the number of seconds since the Unix epoch (January 1, 1970, 00:00:00 UTC). When a server receives a JWT, it compares the current UTC time against the exp value. If the current time is greater than or equal to exp, the token is expired. You can decode and verify any token's timestamps instantly using our JWT Debugger or check expiration countdowns with the JWT Expiry Checker.
Here's what the payload of a JWT with an expiration looks like:
{
"sub": "user-123",
"name": "Asha Patel",
"role": "admin",
"iat": 1716668400,
"exp": 1716672000
}
In this example, iat (issued at) is May 25, 2024 at 23:00 UTC, and exp is May 26, 2024 at 00:00 UTC — giving this token a 1-hour lifetime.
Why Token Expiration Matters
Without expiration, a stolen or leaked JWT grants permanent access until the signing key is rotated — an expensive and disruptive operation. Token expiration limits the damage window:
- Session hijacking mitigation. If an attacker intercepts a token, it becomes useless after expiration. Short-lived tokens (15 minutes) minimize the exposure window.
- Token replay prevention. Expired tokens cannot be replayed against APIs that correctly validate
exp, even if they were captured in transit or extracted from browser storage. - Compliance requirements. Standards like OAuth 2.0, OpenID Connect, and SOC 2 require token expiration as part of secure session management.
- Forced re-authentication. Expiration ensures users periodically re-authenticate, picking up updated permissions, revoked access, or account status changes.
Common exp-Related Bugs
Token expiration seems straightforward, but these bugs appear regularly in production systems:
- Clock skew. If the server clock and the token issuer's clock are even 30 seconds apart, tokens may be rejected as expired before they actually are. Most JWT libraries accept a configurable
leeway(typically 30–60 seconds) to handle this. - Timezone confusion. The
expclaim uses UTC, not local time. Developers who set expiration using local timestamps will generate tokens that expire at the wrong time for users in different timezones. - Confusing
expwithiat. Theiat(issued at) claim records when the token was created. It does not control expiration. Onlyexpdetermines when the token becomes invalid. - Seconds vs. milliseconds. JavaScript's
Date.now()returns milliseconds, but JWTexpuses seconds. Forgetting to divide by 1000 creates tokens that "expire" 1000 years in the future.
// ❌ Wrong — milliseconds
const exp = Date.now() + (60 * 60 * 1000);
// ✅ Correct — seconds
const exp = Math.floor(Date.now() / 1000) + (60 * 60); Practical Unix Timestamp Calculation Examples
When generating tokens across different programming languages and backend frameworks, calculating the exact Unix epoch timestamp in seconds is critical for reliable session management. Here are ready-to-use patterns for common server environments:
Node.js & JavaScript (TypeScript)
In Node.js or browser environments, always divide Date.now() by 1,000 and use Math.floor() to get integer seconds before adding your duration:
// Calculate timestamp for 15 minutes from now
const nowSeconds = Math.floor(Date.now() / 1000);
const expIn15Mins = nowSeconds + (15 * 60);
// Using the popular 'jsonwebtoken' library with relative string duration
const jwt = require('jsonwebtoken');
const token = jwt.sign(
{ sub: 'user-123', role: 'admin' },
process.env.JWT_SECRET,
{ expiresIn: '15m' } // Library automatically calculates the 'exp' Unix timestamp
); Python (PyJWT)
In Python, use the standard time module for integer calculations or timezone-aware datetime objects when encoding with PyJWT:
import time
import jwt
from datetime import datetime, timedelta, timezone
# Method 1: Using integer Unix timestamp with time.time()
exp_timestamp = int(time.time()) + (60 * 60) # 1 hour from now
# Method 2: Using datetime with PyJWT (preferred for readability)
payload = {
"sub": "user-123",
"iat": datetime.now(timezone.utc),
"exp": datetime.now(timezone.utc) + timedelta(hours=1)
}
token = jwt.encode(payload, "secret-key", algorithm="HS256") Go (Golang)
In Go, use the standard time package to obtain Unix seconds when defining custom claims with golang-jwt:
package main
import (
"time"
"github.com/golang-jwt/jwt/v5"
)
func createToken() (string, error) {
claims := jwt.MapClaims{
"sub": "user-123",
"iat": time.Now().Unix(),
"exp": time.Now().Add(24 * time.Hour).Unix(), // 24 hours in seconds
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte("your-secret-key"))
} PHP
PHP's native time() function returns Unix seconds directly, making calculation straightforward:
$issuedAt = time();
$expirationTime = $issuedAt + 3600; // 1 hour from now
$payload = [
'sub' => 'user-123',
'iat' => $issuedAt,
'exp' => $expirationTime
]; Bash / CLI Testing
When drafting shell scripts or testing API endpoints from the command line, use the POSIX date utility to calculate future Unix timestamps:
# Current Unix timestamp in seconds
NOW=$(date +%s)
# Expiry timestamp 1 hour (3600 seconds) in the future
EXP=$(($NOW + 3600))
echo "Current: $NOW | Expiry: $EXP"
Once you've calculated your timestamps, you can instantly test and validate your implementation without writing custom test suites. Use the JWT Generator to craft custom-signed test tokens with specific future or past exp timestamps, verify token lifetime and expiration countdowns with the JWT Expiry Checker, or inspect decoded header and payload claims using the JWT Debugger — all running 100% locally in your browser.
Setting the Right Expiration Time
There is no universal correct value for exp. The right choice depends on your security requirements and user experience trade-offs:
- Access tokens: 5–15 minutes. Short-lived access tokens are the standard for API authentication. They minimize damage from token theft and force regular token refresh cycles.
- Refresh tokens: 7–30 days. Refresh tokens have longer lifetimes because they are stored more securely (HTTP-only cookies, encrypted storage) and are used to obtain new access tokens.
- Single-use tokens: 5–10 minutes. Email verification links, password reset tokens, and magic login links should expire quickly to limit the window for abuse.
- Machine-to-machine tokens: 1–24 hours. Service-to-service authentication can use longer-lived tokens since the risk of human-driven session hijacking is lower.
Checking JWT Expiration Safely
When debugging authentication issues, you need to inspect the exp claim to determine if the token has expired, how much time remains, or whether the expiration was set correctly. The challenge is doing this without exposing sensitive payload data to external servers.
Most online JWT debuggers send your token to a backend server for decoding. That means your user IDs, roles, email addresses, and session tokens are transmitted over the internet and potentially logged by third parties. For production environments, this introduces unnecessary security and compliance risks.
The ZeroData JWT Expiry Checker decodes your token entirely in your browser. It extracts the exp and iat claims, converts them to human-readable local and UTC timestamps, and displays the exact remaining countdown or elapsed time since expiration — all without making a single network request. You can verify this yourself by inspecting the Network tab in DevTools.
For comprehensive token analysis and signature verification, use our client-side JWT Debugger. When setting up automated tests or local development environments, you can also create custom-signed tokens with exact future or past expiration dates using the JWT Generator.
Frequently Asked Questions
- What happens when a JWT expires?
- When the current time exceeds the
expvalue, the token is considered expired. Compliant servers must reject it with a 401 Unauthorized response, forcing the client to obtain a new token via refresh or re-authentication. - What format is the exp claim in?
- The
expclaim uses NumericDate format — a JSON numeric value representing the number of seconds since 1970-01-01T00:00:00Z UTC (the Unix epoch), ignoring leap seconds. For example,1716672000represents May 26, 2024 00:00:00 UTC. - Can I extend a JWT's expiration after it's been issued?
- No. JWTs are cryptographically signed, so modifying any claim — including
exp— invalidates the signature. To extend a session, issue a new token with a later expiration using a refresh token flow. - Is the exp claim required in a JWT?
- The JWT specification (RFC 7519) lists
expas an optional registered claim. However, omitting it creates tokens that never expire, which is a significant security risk. In practice, all production authentication systems should includeexp. - How do I check if a JWT has expired without uploading it to a server?
- Use a client-side JWT tool like the ZeroData JWT Expiry Checker. It decodes the token entirely in your browser, extracts the
expclaim, and compares it against your local system time — without transmitting the token over any network.