Safe, ergonomic helpers for creating, writing, and reading HTTP cookies in Go.
go get github.com/aatuh/cookie/v2- CookieManager with fluent defaults (domain, path, secure, SameSite).
- Signed cookies (HMAC‑SHA256) with key rotation.
- Encrypted cookies (AES‑GCM) with random nonce.
- JSON helpers:
SetJSON/GetJSON. - Prefix enforcement for
__Secure-and__Host-. - SameSite guards ("None" requires
Secure=true). Partitionedattribute support.- Robust
Deleteclears both cookie and companion.sig. - Size guard to avoid exceeding typical browser limits.
m := cookie.NewCookieManager(w).
WithDomain("example.com").
WithPath("/").
WithSecure(true).
WithHTTPOnly(true).
WithSameSite(http.SameSiteLaxMode)
_, _ = m.SetCookie("session", "abc123", 3600)
// later
val := cookie.Get(r, "session")
_ = valsign := cookie.NewHMACSigner([]byte("k1"), []byte("old-k0"))
m := cookie.NewCookieManager(w).WithSecure(true).WithSigner(sign)
_, _ = m.SetSigned("session", "opaque-token", 3600)
v, err := m.ReadSigned(r, "session")
if err != nil { /* handle */ }
fmt.Println(v) // "opaque-token"aead, _ := cookie.NewAEADEncrypter([]byte("32-byte-secret-key-................"), nil)
m := cookie.NewCookieManager(w).WithSecure(true).WithEncrypter(aead)
// store bytes securely
_, _ = m.SetEncrypted("prefs", []byte("dark:on"), 30*24*3600)
b, _ := m.ReadDecrypted(r, "prefs")
fmt.Println(string(b))type Prefs struct{ Theme string `json:"theme"` }
aead, _ := cookie.NewAEADEncrypter([]byte("32-byte-secret-key-................"), nil)
m := cookie.NewCookieManager(w).WithSecure(true).WithEncrypter(aead)
_, _ = m.SetJSON("prefs", Prefs{Theme: "dark"}, 90*24*3600)
var p Prefs
_ = m.GetJSON(r, "prefs", &p)__Secure-<name>requiresSecure=true.__Host-<name>requiresSecure=true,Path=/, and noDomain.SameSite=NonerequiresSecure=true.
Violations return descriptive errors.
m := cookie.NewCookieManager(w).WithSecure(true).WithPartitioned(true)
_, _ = m.SetCookie("__Host-part", "v", 300)Partitioned helps limit cross‑site tracking of third‑party cookies. This
field exists in net/http.
m.Delete("session") // sets Max-Age=-1 and clears "session.sig" if presentCreate HMACSigner with multiple keys. The first key signs new cookies; all
keys verify old ones.
sign := cookie.NewHMACSigner([]byte("new"), []byte("old1"), []byte("old0"))// Construction
NewCookieManager(w http.ResponseWriter) *Manager
// Defaults
(*Manager).WithDomain(string) *Manager
(*Manager).WithPath(string) *Manager
(*Manager).WithHTTPOnly(bool) *Manager
(*Manager).WithSecure(bool) *Manager
(*Manager).WithSameSite(http.SameSite) *Manager
(*Manager).WithPartitioned(bool) *Manager
(*Manager).WithSigner(Signer) *Manager
(*Manager).WithEncrypter(Encrypter) *Manager
// Operations
(*Manager).SetCookie(name, value string, ttlSeconds int) (*http.Cookie, error)
(*Manager).Delete(name string)
// Signed
(*Manager).SetSigned(name, plain string, ttlSeconds int) (*http.Cookie, error)
(*Manager).ReadSigned(r *http.Request, name string) (string, error)
// Encrypted
(*Manager).SetEncrypted(name string, plain []byte, ttlSeconds int) (*http.Cookie, error)
(*Manager).ReadDecrypted(r *http.Request, name string) ([]byte, error)
// JSON
(*Manager).SetJSON(name string, v any, ttlSeconds int) (*http.Cookie, error)
(*Manager).GetJSON(r *http.Request, name string, out any) error
// Utilities
GetCookieFromRequest(r *http.Request, name string) *http.Cookie
Get(r *http.Request, name string) stringThe package includes opinionated builders for common cookie types. These thin wrappers sit on top of Manager and preconfigure secure, standards-compliant flags for typical scenarios: essential session cookies, analytics cookies, and third-party/marketing cookies.
All builders are stdlib-only, composable, and safe to use across handlers. Prefer these when you want consistent, low-boilerplate cookie handling without footguns.
b := NewEssential(w, "session").WithTTL(24*time.Hour)
_ = b.Set("opaque-session-id")a := NewAnalytics(w, "ga_cid").WithConsentChecker(func(r *http.Request) bool {
return cookie.Get(r, "consent.analytics") == "true"
})
_ = a.SetID(r, "cid-123") // will no-op with ErrConsentNotGranted if gatedt := NewThirdParty(w, "__Secure-ad_id").
WithDomain(".example.com").WithPartitioned(true)
_ = t.Set("some-value")- Keep payloads small; browsers commonly limit name+value to ~4096 bytes.
- For large state, prefer server‑side sessions and store a short opaque identifier in the cookie.
- Use
Secure=truein production andSameSite=LaxorStrictunless a cross‑site flow requiresNone.