Cookies and sessions are fundamental to web applications for maintaining user state. Go provides built-in support for cookies, and sessions can be implemented using various storage backends. This guide covers both. For more context, see Go Installation Guide, Go Ecosystem Overview, Go Best Practices.
Working with Cookies
Setting Cookies
package main
import (
"fmt"
"log"
"net/http"
"time"
)
func setCookieHandler(w http.ResponseWriter, r *http.Request) {
// Create a cookie
cookie := &http.Cookie{
Name: "user_id",
Value: "12345",
Path: "/",
Expires: time.Now().Add(24 * time.Hour),
HttpOnly: true,
Secure: false, // Set to true in production with HTTPS
}
// Set the cookie
http.SetCookie(w, cookie)
fmt.Fprintf(w, "Cookie set")
}
func main() {
http.HandleFunc("/set-cookie", setCookieHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Reading Cookies
package main
import (
"fmt"
"log"
"net/http"
)
func readCookieHandler(w http.ResponseWriter, r *http.Request) {
// Read specific cookie
cookie, err := r.Cookie("user_id")
if err != nil {
fmt.Fprintf(w, "Cookie not found")
return
}
fmt.Fprintf(w, "User ID: %s", cookie.Value)
}
func readAllCookiesHandler(w http.ResponseWriter, r *http.Request) {
// Read all cookies
cookies := r.Cookies()
for _, cookie := range cookies {
fmt.Fprintf(w, "%s=%s\n", cookie.Name, cookie.Value)
}
}
func main() {
http.HandleFunc("/read-cookie", readCookieHandler)
http.HandleFunc("/read-all", readAllCookiesHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Cookie Options
package main
import (
"fmt"
"log"
"net/http"
"time"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// Session cookie (expires when browser closes)
sessionCookie := &http.Cookie{
Name: "session",
Value: "abc123",
Path: "/",
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteLaxMode,
}
// Persistent cookie (expires at specific time)
persistentCookie := &http.Cookie{
Name: "remember_me",
Value: "[email protected]",
Path: "/",
Expires: time.Now().Add(30 * 24 * time.Hour),
HttpOnly: true,
Secure: true,
}
http.SetCookie(w, sessionCookie)
http.SetCookie(w, persistentCookie)
fmt.Fprintf(w, "Cookies set")
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
Deleting Cookies
package main
import (
"fmt"
"log"
"net/http"
"time"
)
func deleteCookieHandler(w http.ResponseWriter, r *http.Request) {
// Delete cookie by setting MaxAge to -1
cookie := &http.Cookie{
Name: "user_id",
Value: "",
Path: "/",
MaxAge: -1,
}
http.SetCookie(w, cookie)
fmt.Fprintf(w, "Cookie deleted")
}
func main() {
http.HandleFunc("/delete-cookie", deleteCookieHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Session Management
In-Memory Session Store
package main
import (
"fmt"
"log"
"net/http"
"sync"
"time"
)
type Session struct {
ID string
Data map[string]interface{}
ExpiresAt time.Time
}
type SessionStore struct {
sessions map[string]*Session
mu sync.RWMutex
}
func NewSessionStore() *SessionStore {
return &SessionStore{
sessions: make(map[string]*Session),
}
}
func (ss *SessionStore) Create(id string) *Session {
ss.mu.Lock()
defer ss.mu.Unlock()
session := &Session{
ID: id,
Data: make(map[string]interface{}),
ExpiresAt: time.Now().Add(24 * time.Hour),
}
ss.sessions[id] = session
return session
}
func (ss *SessionStore) Get(id string) *Session {
ss.mu.RLock()
defer ss.mu.RUnlock()
session, ok := ss.sessions[id]
if !ok || time.Now().After(session.ExpiresAt) {
return nil
}
return session
}
func (ss *SessionStore) Delete(id string) {
ss.mu.Lock()
defer ss.mu.Unlock()
delete(ss.sessions, id)
}
var store = NewSessionStore()
func loginHandler(w http.ResponseWriter, r *http.Request) {
sessionID := "session_" + fmt.Sprintf("%d", time.Now().UnixNano())
session := store.Create(sessionID)
session.Data["user_id"] = "12345"
session.Data["username"] = "john"
cookie := &http.Cookie{
Name: "session_id",
Value: sessionID,
Path: "/",
HttpOnly: true,
Secure: true,
}
http.SetCookie(w, cookie)
fmt.Fprintf(w, "Logged in")
}
func profileHandler(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie("session_id")
if err != nil {
http.Error(w, "Not logged in", http.StatusUnauthorized)
return
}
session := store.Get(cookie.Value)
if session == nil {
http.Error(w, "Session expired", http.StatusUnauthorized)
return
}
username := session.Data["username"]
fmt.Fprintf(w, "Welcome, %v", username)
}
func main() {
http.HandleFunc("/login", loginHandler)
http.HandleFunc("/profile", profileHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Session Middleware
package main
import (
"context"
"fmt"
"log"
"net/http"
)
type contextKey string
const sessionKey contextKey = "session"
func sessionMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie("session_id")
if err != nil {
http.Error(w, "Not logged in", http.StatusUnauthorized)
return
}
session := store.Get(cookie.Value)
if session == nil {
http.Error(w, "Session expired", http.StatusUnauthorized)
return
}
// Add session to context
ctx := context.WithValue(r.Context(), sessionKey, session)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func protectedHandler(w http.ResponseWriter, r *http.Request) {
session := r.Context().Value(sessionKey).(*Session)
fmt.Fprintf(w, "User: %v", session.Data["username"])
}
func main() {
http.Handle("/protected", sessionMiddleware(http.HandlerFunc(protectedHandler)))
log.Fatal(http.ListenAndServe(":8080", nil))
}
Practical Examples
User Authentication
package main
import (
"crypto/sha256"
"fmt"
"log"
"net/http"
"time"
)
type User struct {
ID string
Username string
Password string // Should be hashed in production
}
var users = map[string]*User{
"john": {ID: "1", Username: "john", Password: "password123"},
}
func hashPassword(password string) string {
hash := sha256.Sum256([]byte(password))
return fmt.Sprintf("%x", hash)
}
func loginHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
username := r.FormValue("username")
password := r.FormValue("password")
user, ok := users[username]
if !ok || user.Password != password {
http.Error(w, "Invalid credentials", http.StatusUnauthorized)
return
}
// Create session
sessionID := fmt.Sprintf("session_%d", time.Now().UnixNano())
session := store.Create(sessionID)
session.Data["user_id"] = user.ID
session.Data["username"] = user.Username
// Set cookie
cookie := &http.Cookie{
Name: "session_id",
Value: sessionID,
Path: "/",
HttpOnly: true,
Secure: true,
MaxAge: 86400, // 24 hours
}
http.SetCookie(w, cookie)
fmt.Fprintf(w, "Login successful")
}
func logoutHandler(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie("session_id")
if err == nil {
store.Delete(cookie.Value)
}
// Clear cookie
cookie = &http.Cookie{
Name: "session_id",
Value: "",
Path: "/",
MaxAge: -1,
}
http.SetCookie(w, cookie)
fmt.Fprintf(w, "Logged out")
}
func main() {
http.HandleFunc("/login", loginHandler)
http.HandleFunc("/logout", logoutHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Remember Me Functionality
package main
import (
"fmt"
"log"
"net/http"
"time"
)
func loginWithRememberHandler(w http.ResponseWriter, r *http.Request) {
username := r.FormValue("username")
rememberMe := r.FormValue("remember_me") == "on"
// Create session
sessionID := fmt.Sprintf("session_%d", time.Now().UnixNano())
session := store.Create(sessionID)
session.Data["username"] = username
// Set session cookie
sessionCookie := &http.Cookie{
Name: "session_id",
Value: sessionID,
Path: "/",
HttpOnly: true,
Secure: true,
}
http.SetCookie(w, sessionCookie)
// Set remember me cookie if requested
if rememberMe {
rememberCookie := &http.Cookie{
Name: "remember_me",
Value: username,
Path: "/",
Expires: time.Now().Add(30 * 24 * time.Hour),
HttpOnly: true,
Secure: true,
}
http.SetCookie(w, rememberCookie)
}
fmt.Fprintf(w, "Login successful")
}
func main() {
http.HandleFunc("/login", loginWithRememberHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Best Practices
✅ Good Practices
// Set HttpOnly flag to prevent JavaScript access
cookie := &http.Cookie{
HttpOnly: true,
}
// Set Secure flag for HTTPS
cookie := &http.Cookie{
Secure: true,
}
// Set SameSite to prevent CSRF
cookie := &http.Cookie{
SameSite: http.SameSiteLaxMode,
}
// Use strong session IDs
sessionID := generateSecureRandomID()
// Expire sessions properly
session.ExpiresAt = time.Now().Add(24 * time.Hour)
// Hash passwords
hashedPassword := hashPassword(password)
❌ Anti-Patterns
// Don't store sensitive data in cookies
cookie.Value = userPassword // Wrong!
// Don't use weak session IDs
sessionID := "session_1" // Predictable
// Don't forget HttpOnly flag
cookie := &http.Cookie{
HttpOnly: false, // Vulnerable to XSS
}
// Don't store sessions in memory only
// Use persistent storage for production
// Don't ignore session expiration
// Always check expiry time
Resources
- Go http.Cookie Documentation
- HTTP State Management Mechanism
- OWASP Session Management
- Go Security Best Practices
Summary
Cookies and sessions are essential for web applications:
- Use cookies for client-side storage
- Implement sessions for server-side state
- Always set HttpOnly and Secure flags
- Use SameSite to prevent CSRF
- Expire sessions properly
- Hash passwords and use strong session IDs
With these practices, you can build secure, user-friendly web applications in Go.
Comments