---
title: "Add JWT Authentication to Your Caddy API"
description: "Secure your Caddy API using JWT authentication with JWKS."
canonicalUrl: "https://zuplo.com/use-cases/api-key-auth/go/caddy/jwt-backend"
framework: "Caddy"
language: "Go"
authStrategy: "JWT with JWKS"
pageType: use-case
---

# Add JWT Authentication to Your Caddy API

Secure your Caddy API using JWT authentication with JWKS.

## How Zuplo Handles It

Let Zuplo issue short-lived JWTs signed with a JWKS your Caddy backend can verify — no long-lived API keys touch your origin.

## Caddy Backend Code

```go
package main

import (
    "context"
    "log"
    "net/http"
    "github.com/caddyserver/caddy/v2"
    "github.com/caddyserver/caddy/v2/modules/caddyhttp"
    "github.com/MicahParks/keyfunc" // JWKS library for Go
    "github.com/golang-jwt/jwt/v4"  // JWT library for Go
)

const (
    ISSUER = "https://my-api-a32f34.zuplo.api/__zuplo/issuer"
)

func main() {
    caddy := &caddy.App{
        Name: "jwt-secure-api",
    }

    httpApp := &caddyhttp.App{}
    httpApp.AddRoute(caddyhttp.Route{
        Handlers: caddyhttp.HandlerFuncs{
            jwtMiddleware,
            protectedEndpoint,
        },
    })

    caddy.Apps = append(caddy.Apps, httpApp)

    if err := caddy.Start(context.Background()); err != nil {
        log.Fatal(err)
    }
}

func jwtMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        tokenStr := r.Header.Get("Authorization")
        if tokenStr == "" {
            http.Error(w, "No token provided", http.StatusUnauthorized)
            return
        }

        token, err := jwt.Parse(tokenStr[len("Bearer "):], keystoreFunc)
        if err != nil || !token.Valid {
            http.Error(w, "Invalid token", http.StatusUnauthorized)
            return
        }

        claims, ok := token.Claims.(jwt.MapClaims)
        if !ok || !claims.VerifyIssuer(ISSUER, true) {
            http.Error(w, "Invalid token issuer", http.StatusUnauthorized)
            return
        }

        next.ServeHTTP(w, r)
    })
}

func keystoreFunc(token *jwt.Token) (interface{}, error) {
    jwks, err := keyfunc.Get(ISSUER+"/.well-known/jwks.json")
    if err != nil {
        return nil, err
    }
    return jwks.Keyfunc(token)
}

func protectedEndpoint(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("Access granted"))
}
```

## Example Request

```bash
curl -X GET \
  'https://your-api.zuplo.dev/your-route' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

## Learn More

- [API Key Authentication on Zuplo](https://zuplo.com/docs/policies/api-key-auth-inbound)
- [JWT Authentication on Zuplo](https://zuplo.com/docs/policies/open-id-jwt-auth-inbound)
- [All use cases](https://zuplo.com/use-cases)
