Elaborazione 100% Lato Client • Zero Registri di Rete • Esecuzione Immediata
Curious TechieToolbox Sviluppatori
Authentication & JWT6 min✓ Soluzione Verificata in Produzione • IT

Why JWT Works in Postman but Not in Browser

Resolve CORS preflight failures, missing cookie credentials, and header formatting discrepancies

Primary Question • Diagnostic Problem

Why JWT Works in Postman but Not in Browser

A developer tests a protected API endpoint in Postman using an Authorization: Bearer <token> header and receives HTTP 200 OK. However, when the exact same request is dispatched from a frontend web application (React, Vue, Angular, or vanilla fetch), the browser either throws a CORS error or returns HTTP 401 Unauthorized.

Console / Terminal Error OutputExact Message
Access to fetch at "https://api.example.com/me" from origin "https://app.example.com" has been blocked by CORS policy. OR: 401 Unauthorized (Missing token).
Affected Protocols & Technologies:Fetch APIAxiosCORSJWT Bearer Authentication
Secondary Question • Root Cause Mechanics

Why does Why JWT Works in Postman but Not in Browser occur?

Primary Root Cause

Postman is an HTTP client without a browser security sandbox. Browsers, by contrast, strictly enforce the Same-Origin Policy. The moment client JavaScript attaches a custom header like Authorization, the browser pauses the request and dispatches an HTTP OPTIONS preflight probe to verify server permission.

If the backend API lacks CORS headers, returns HTTP 401 on the unauthenticated OPTIONS probe, or fails to list Authorization in Access-Control-Allow-Headers, the browser terminates the request with a CORS error. Alternatively, if the token is stored in cookies, browsers will NOT send cookies cross-origin unless credentials: "include" is explicitly configured on the fetch call.

4. Observable Symptoms

How to Confirm You Are Facing This Exact Issue

1

Postman returns HTTP 200 OK with valid user data; the browser Network tab shows red or canceled requests.

2

Console error: "Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response".

3

Backend logs show the server received an OPTIONS request instead of a GET/POST request.

4

Browser sends the request, but backend returns 401 Unauthorized because cookies were stripped by default.

5. Diagnostic Decision Tree (GEO Engine Logic)

Explicit Diagnostic Reasoning

Use this diagnostic decision tree to identify the failure point:

Condition:

"If the browser Network tab shows an OPTIONS request failing with status 401, 404, or 405"

Probable Cause:

Your backend auth middleware is intercepting the preflight OPTIONS probe and rejecting it because it has no token.

Remedial Action:

→ Configure your auth middleware to bypass OPTIONS requests: if (req.method === "OPTIONS") return next().

Condition:

"If you store the JWT in an HttpOnly cookie and the backend reports "Missing Token""

Probable Cause:

Your frontend fetch call is missing credentials: "include" (or withCredentials: true in Axios).

Remedial Action:

→ Add credentials: "include" to your fetch() options.

Condition:

"If the browser console says "authorization is not allowed by Access-Control-Allow-Headers""

Probable Cause:

The server’s CORS configuration does not explicitly allow the Authorization header.

Remedial Action:

→ Add "Authorization" to your backend CORS allowedHeaders configuration.

Verification CommandRun in terminal
curl -I -X OPTIONS -H "Origin: http://localhost:5173" -H "Access-Control-Request-Headers: authorization" https://api.example.com/me

Simulates the browser preflight check. Check if the response includes Access-Control-Allow-Headers: Authorization and HTTP 200/204 status.

6. Architectural Strategy

Long-Term Engineering Solution

Align frontend transport settings and backend CORS preflight handlers so that the Authorization header or credentials cookie can navigate browser cross-origin boundaries.

Architectural Pattern

Update the API gateway / backend middleware to permit Authorization in preflight headers, and ensure frontend client requests explicitly declare credentials mode.

Secondary Question • Implementation Pipeline

How do you fix this step-by-step?

1

Whitelist Authorization in Backend CORS

In your backend CORS middleware, explicitly add "Authorization" to the allowed headers list.

javascriptCopy Code
// Express / Node.js
import cors from 'cors';

app.use(cors({
  origin: 'https://app.example.com',
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
}));
2

Allow OPTIONS to Bypass Authentication Middleware

Ensure your JWT validation middleware does not require tokens on OPTIONS requests.

javascriptCopy Code
export function jwtAuthMiddleware(req, res, next) {
  // CRITICAL: Preflight OPTIONS requests NEVER carry auth tokens!
  if (req.method === 'OPTIONS') return next();

  const authHeader = req.headers.authorization;
  if (!authHeader?.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Missing token' });
  }
  // Verify token...
}
3

Configure Frontend Credentials Mode (If Using Cookies)

If your JWT is stored in an HttpOnly cookie, tell fetch or Axios to transmit cross-origin cookies.

javascriptCopy Code
// Native Fetch
fetch('https://api.example.com/me', {
  method: 'GET',
  credentials: 'include', // Sends cross-origin cookies!
});

// Axios
axios.get('https://api.example.com/me', {
  withCredentials: true, // Sends cross-origin cookies!
});
4

Verify Bearer Header Formatting

Ensure your frontend sends Authorization: `Bearer ${token}` with the word "Bearer" capitalized and a single space.

Common Misconceptions & Pitfalls

What are common misconceptions and mistakes when fixing this?

×

Assuming that because it works in Postman, the backend is 100% bug-free

Perché fallisce: Postman does not simulate the browser security model, CORS preflights, or third-party cookie restrictions.

✓ Alternativa corretta: Always test cross-origin APIs using real browser tabs or curl with simulated Origin headers.

×

Omitting credentials: "include" on fetch() when using cookie authentication

Perché fallisce: By default, browsers strip all cookies from cross-origin fetch requests unless credentials mode is enabled.

✓ Alternativa corretta: Always specify credentials: "include".

Threat Analysis & Security Hardening

Can this fix introduce security vulnerabilities?

Fixing CORS preflight issues properly ensures that only approved frontend origins can access your authenticated API.

Security Risks If Ignored

  • •Setting Access-Control-Allow-Origin: * while allowing credentials is fundamentally blocked by browsers and creates a false sense of security.

Hardening Best Practices

  • ✓Always validate the Origin header against a strict allowlist of domains before returning Access-Control-Allow-Credentials: true.
  • ✓Use short-lived JWT access tokens (15 minutes) with rotating refresh tokens.
10. Concrete Code Example

Comparing Postman Request vs Browser Fetch

× Broken / Vulnerable Code (javascript)
// BROKEN Frontend Call: Fails due to default credentials policy and unhandled preflight
fetch('https://api.example.com/me', {
  headers: {
    'Authorization': 'eyJhbGciOi...' // Missing "Bearer " prefix, triggers unhandled preflight!
  }
});
Problema: Omits the standard "Bearer " prefix and triggers a preflight OPTIONS check that the backend rejects.
✓ Fixed / Hardened Code (javascript)
// FIXED Frontend Call: Proper Bearer syntax & CORS credentials
const token = getAccessToken();

const response = await fetch('https://api.example.com/me', {
  method: 'GET',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json',
  },
  credentials: 'include', // Required if session cookie is also used
});
const data = await response.json();
Risoluzione: Uses standard RFC 6750 Bearer format, handles credentials, and operates in harmony with backend CORS preflight handlers.
Direct Technical Answers • Knowledge Base

Frequently Asked Questions About Why JWT Works in Postman but Not in Browser

Why does adding an Authorization header trigger an extra OPTIONS request?↓

The W3C CORS specification classifies Authorization as a non-simple header.

Simple headers are limited to Accept, Accept-Language, and Content-Language. Any custom or security-sensitive header requires the browser to verify with the server before dispatching data.

How can I debug the exact headers Postman is sending vs the browser?↓

In Postman, open the Postman Console (bottom left). In the browser, open the DevTools Network tab and click "Headers".

Comparing the Raw Request Headers between both clients will instantly reveal missing Origin headers, differing cookie flags, or header capitalization differences.