DEV Community

Cover image for Clerk JWT 401 on the server? Check these 3 env vars first
FetchSandbox
FetchSandbox

Posted on

Clerk JWT 401 on the server? Check these 3 env vars first

A Clerk login can look fine in the browser and still fail on your server with a 401.

The frontend has a user. getToken() returns something. The request includes:

Authorization: Bearer <token>
Enter fullscreen mode Exit fullscreen mode

Then FastAPI, Express, or your API route rejects it.

Before rewriting your auth code, check this:

CLERK_PUBLISHABLE_KEY
CLERK_SECRET_KEY
CLERK_JWT_ISSUER
Enter fullscreen mode Exit fullscreen mode

They need to come from the same Clerk instance.

The bug

This can happen when you have multiple Clerk projects open:

Frontend:
  CLERK_PUBLISHABLE_KEY = pk_test_from_project_A

Backend:
  CLERK_SECRET_KEY = sk_test_from_project_B
  CLERK_JWT_ISSUER = https://project-c.clerk.accounts.dev
Enter fullscreen mode Exit fullscreen mode

Each value can look valid by itself.

But the token was minted by one Clerk instance, and your backend is trying to verify it against another one. That is enough for server-side verification to fail.

You might see:

JWT issuer mismatch
invalid issuer
JWKS key not found
invalid signature
401 unauthorized
Enter fullscreen mode Exit fullscreen mode

Different library, same root issue.

The quick fix

Open one Clerk dashboard project and copy all auth values again from the same place.

Then restart both servers.

# frontend
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...

# backend
CLERK_SECRET_KEY=sk_test_...
CLERK_JWT_ISSUER=https://same-instance.clerk.accounts.dev
Enter fullscreen mode Exit fullscreen mode

Do not only refresh the browser. Dev servers often keep old env values until the process restarts.

Why agents miss this

An AI coding agent can write the shape of a Clerk integration:

add middleware
read bearer token
verify JWT
sync user
protect route
Enter fullscreen mode Exit fullscreen mode

But it will not know you pasted keys from two Clerk projects unless it checks the running path.

This is why I like running a small auth workflow before wiring the app.

Receipt

I ran FetchSandbox's Clerk user_signup workflow as the receipt layer:

Workflow: clerk/user_signup
Result: passed
Steps: 4/4
Duration: 77.44 ms
Verified webhooks: user.created, user.updated
Timeline: https://fetchsandbox.com/runs/7aa06cff84?flow=run_b8399f77-352c-4fe4-ad50-24e3060f8d8d
Enter fullscreen mode Exit fullscreen mode

That workflow proves the user lifecycle surface:

POST /users
GET /users/{id}
PATCH /users/{id}
verify user.created + user.updated
Enter fullscreen mode Exit fullscreen mode

It does not replace your real Clerk project. It gives your agent or teammate a runnable auth flow before they touch app code.

Takeaway

If Clerk works in the browser but your server returns 401, check the boring thing first:

publishable key
secret key
issuer URL
Enter fullscreen mode Exit fullscreen mode

Same instance. Same environment. Then restart.

FetchSandbox MCP setup if you want the agent to run the workflow first:

{
  "mcpServers": {
    "fetchsandbox": {
      "command": "npx",
      "args": ["-y", "fetchsandbox-mcp"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)