---
title: "Protect a Go net/http Service"
url: "https://docs.caracal.run/v1.0/guides/protect-nethttp/"
markdown_url: "https://docs.caracal.run/markdown/v1.0/guides/protect-nethttp.md"
description: "Wrap Go HTTP handlers with the nethttp adapter middleware to verify mandates and attach claims to context.Context."
page_type: "page"
concepts: []
requires: []
---

# Protect a Go net/http Service

Canonical URL: https://docs.caracal.run/v1.0/guides/protect-nethttp/
Markdown URL: https://docs.caracal.run/markdown/v1.0/guides/protect-nethttp.md
Description: Wrap Go HTTP handlers with the nethttp adapter middleware to verify mandates and attach claims to context.Context.
Page type: page
Concepts: none
Requires: none

---

Use the Go net/http adapter when a Go service should verify Caracal mandates at the handler boundary.

## Prerequisites

* A mandate-aware resource with a stable audience and route scopes.
* STS issuer, zone ID, and a production Redis revocation consumer.
* Request deadlines on inbound handlers so JWKS or revocation work is cancelable.

## Install

```bash
go get github.com/garudex-labs/caracal/packages/adapters/nethttp/go
go get github.com/garudex-labs/caracal/packages/revocation/go
```

## Wrap a handler

```go
package main

import (
	"encoding/json"
	"net/http"
	"time"

	nethttp "github.com/garudex-labs/caracal/packages/adapters/nethttp/go"
	revocation "github.com/garudex-labs/caracal/packages/revocation/go"
	verify "github.com/garudex-labs/caracal/packages/verify/go"
)

func main() {
	revocations := revocation.NewInMemoryStore(24 * time.Hour)

	verifier := verify.NewVerifier(verify.Options{
		Issuer:      "https://sts.pipernet.example",
		Audience:    "resource://pipernet",
		ZoneID:      "0195f2a9-1b22-7c3d-9e4f-5a6b7c8d9e0f",
		Revocations: revocations,
	})

	protected := nethttp.VerifierMiddleware(verifier.Require(verify.Options{
		RequiredScopes:  []string{"pipernet:read"},
		RequiredTargets: []string{"resource://pipernet"},
	}))(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		claims, ok := nethttp.ClaimsFromContext(r.Context())
		if !ok {
			http.Error(w, "missing claims", http.StatusUnauthorized)
			return
		}
		_ = json.NewEncoder(w).Encode(map[string]string{"subject": claims.Sub})
	}))

	http.Handle("/reports", protected)
	_ = http.ListenAndServe(":8080", nil)
}
```

## Enforce constraints

| Option                 | Use it for                     |
| ---------------------- | ------------------------------ |
| `RequiredScopes`       | Route or operation permission. |
| `RequiredTargets`      | Resource target matching.      |
| `RequireSession`       | Session-bound endpoints.       |
| `RequireDelegation`    | Delegated-only endpoints.      |
| `RequireChainContains` | Application path requirements. |
| `MaxHopCount`          | Delegation depth limit.        |

## Production revocation

The in-memory store does not share revocations across instances. Use the Redis revocation backend and consume `caracal.sessions.revoke` for production resource servers.

## Validate

1. Call without a bearer token and expect `401`.
2. Call with a valid mandate and expect the handler response.
3. Remove a required scope and expect `403`.
4. Mark the session revoked and expect `session_revoked`.

Expected result: `401` identifies an unaccepted credential, `403` identifies accepted but insufficient authority, and `ClaimsFromContext` succeeds only inside protected handlers.

:::caution[Failure point: middleware order]
Place Caracal verification before handlers that read bodies or perform work. Logging may wrap it, but authentication-dependent middleware must run after it.
:::

For exact Go fields, use [net/http Adapter reference](/v1.0/sdks/adapters/nethttp/).

## Next Step

Connect the Redis revocation consumer and validate it with [Test Caracal Integrations](/v1.0/guides/testing/).
