Documentation
¶
Overview ¶
Package muxhandlers provides HTTP middleware handlers for the mux router.
CORS Middleware ¶
CORSMiddleware implements the full CORS protocol per the Fetch Standard. It validates the Origin header (RFC 6454), handles preflight OPTIONS requests, and sets the appropriate response headers.
mw, err := muxhandlers.CORSMiddleware(r, muxhandlers.CORSConfig{
AllowedOrigins: []string{"https://example.com"},
AllowCredentials: true,
})
if err != nil {
log.Fatal(err)
}
r.Use(mw)
Basic Auth Middleware ¶
BasicAuthMiddleware implements HTTP Basic Authentication per RFC 7617. Credentials can be validated via a dynamic callback or a static map. Static credential comparison uses constant-time comparison to prevent timing attacks.
mw, err := muxhandlers.BasicAuthMiddleware(muxhandlers.BasicAuthConfig{
Realm: "My App",
Credentials: map[string]string{
"admin": "secret",
},
})
if err != nil {
log.Fatal(err)
}
r.Use(mw)
Bearer Auth Middleware ¶
BearerAuthMiddleware implements HTTP Bearer Token Authentication per RFC 6750. It extracts the token from the Authorization header and validates it using a user-provided function. When the token is missing, malformed, or invalid, the middleware responds with 401 Unauthorized and a WWW-Authenticate: Bearer header per RFC 6750 Section 3. The ValidateFunc receives the full request, allowing token validation based on route variables, headers, or other request context.
mw, err := muxhandlers.BearerAuthMiddleware(muxhandlers.BearerAuthConfig{
Realm: "My API",
ValidateFunc: func(r *http.Request, token string) bool {
return token == expectedToken
},
})
if err != nil {
log.Fatal(err)
}
r.Use(mw)
Proxy Headers Middleware ¶
ProxyHeadersMiddleware populates request fields from reverse proxy headers when the request originates from a trusted proxy. It sets r.RemoteAddr from X-Forwarded-For or X-Real-IP, r.URL.Scheme from X-Forwarded-Proto or X-Forwarded-Scheme, and r.Host from X-Forwarded-Host. When EnableForwarded is true, the RFC 7239 Forwarded header is also parsed as a lowest-priority fallback. A trusted proxy list (IPs and CIDRs) restricts which peers are allowed to set these headers, preventing spoofing from untrusted clients. When TrustedProxies is empty, DefaultTrustedProxies (RFC 1918, RFC 4193, and loopback ranges) is used.
mw, err := muxhandlers.ProxyHeadersMiddleware(muxhandlers.ProxyHeadersConfig{
TrustedProxies: []string{"10.0.0.0/8", "172.16.0.0/12"},
EnableForwarded: true,
})
if err != nil {
log.Fatal(err)
}
r.Use(mw)
Recovery Middleware ¶
RecoveryMiddleware recovers from panics in downstream handlers, returns 500 Internal Server Error to the client, and optionally invokes a custom log function with the request and recovered value.
r.Use(muxhandlers.RecoveryMiddleware(muxhandlers.RecoveryConfig{
LogFunc: func(r *http.Request, err any) {
log.Printf("panic: %v %s", err, r.URL.Path)
},
}))
Request ID Middleware ¶
RequestIDMiddleware generates or propagates a unique request identifier. The ID is set on the request header, the response header, and the request context. Downstream handlers can retrieve it with RequestIDFromContext. By default it generates UUID v4 values using github.com/google/uuid. Use GenerateUUIDv7 for time-ordered IDs (RFC 9562). The GenerateFunc receives the current request, allowing ID generation based on request context.
r.Use(muxhandlers.RequestIDMiddleware(muxhandlers.RequestIDConfig{
TrustIncoming: true,
}))
Time-ordered UUID v7:
r.Use(muxhandlers.RequestIDMiddleware(muxhandlers.RequestIDConfig{
GenerateFunc: muxhandlers.GenerateUUIDv7,
}))
Request Size Limit Middleware ¶
RequestSizeLimitMiddleware rejects request bodies that exceed a maximum size. It wraps r.Body with http.MaxBytesReader, which returns 413 Request Entity Too Large when the limit is exceeded.
mw, err := muxhandlers.RequestSizeLimitMiddleware(muxhandlers.RequestSizeLimitConfig{
MaxBytes: 1 << 20, // 1 MiB
})
if err != nil {
log.Fatal(err)
}
r.Use(mw)
Timeout Middleware ¶
TimeoutMiddleware limits handler execution time by wrapping the handler with http.TimeoutHandler. It returns 503 Service Unavailable when the handler does not complete within the configured duration.
mw, err := muxhandlers.TimeoutMiddleware(muxhandlers.TimeoutConfig{
Duration: 30 * time.Second,
})
if err != nil {
log.Fatal(err)
}
r.Use(mw)
Compression Middleware ¶
CompressionMiddleware compresses response bodies using gzip or deflate when the client advertises support via the Accept-Encoding header. Gzip is preferred over deflate when both are accepted. It uses sync.Pool instances to reuse writers for performance. Compression is skipped for inherently compressed content types (images, video, audio, archives).
mw, err := muxhandlers.CompressionMiddleware(muxhandlers.CompressionConfig{
Level: gzip.BestSpeed,
MinLength: 1024,
})
if err != nil {
log.Fatal(err)
}
r.Use(mw)
Security Headers Middleware ¶
SecurityHeadersMiddleware sets common security response headers with sensible defaults. Headers are set before calling the next handler. By default it sets X-Content-Type-Options: nosniff, X-Frame-Options: DENY, and Referrer-Policy: strict-origin-when-cross-origin. HSTS, CSP, Permissions-Policy, and Cross-Origin-Opener-Policy headers are opt-in.
mw, err := muxhandlers.SecurityHeadersMiddleware(muxhandlers.SecurityHeadersConfig{
HSTSMaxAge: 63072000,
HSTSIncludeSubDomains: true,
HSTSPreload: true,
})
if err != nil {
log.Fatal(err)
}
r.Use(mw)
Method Override Middleware ¶
MethodOverrideMiddleware allows clients to override the HTTP method via a configurable header. By default only POST requests are eligible for override; use OriginalMethods to allow other methods. The first non-empty header value from HeaderNames is uppercased and checked against the allowed set. When allowed, r.Method is updated and the header is removed from the request. By default it checks X-HTTP-Method-Override, X-Method-Override, and X-HTTP-Method in that order.
mw, err := muxhandlers.MethodOverrideMiddleware(muxhandlers.MethodOverrideConfig{})
if err != nil {
log.Fatal(err)
}
r.Use(mw)
Content-Type Check Middleware ¶
ContentTypeCheckMiddleware validates that requests carry a matching Content-Type header. Matching is case-insensitive and ignores parameters such as charset. It returns 415 Unsupported Media Type when the Content-Type is missing or does not match any of the allowed types. By default it checks POST, PUT, and PATCH requests.
mw, err := muxhandlers.ContentTypeCheckMiddleware(muxhandlers.ContentTypeCheckConfig{
AllowedTypes: []string{"application/json"},
})
if err != nil {
log.Fatal(err)
}
r.Use(mw)
Server Middleware ¶
ServerMiddleware sets server identification response headers. It sets X-Server-Hostname with the machine hostname, resolved once at factory time via os.Hostname. Use the Hostname field to provide a static value instead.
mw, err := muxhandlers.ServerMiddleware(muxhandlers.ServerConfig{})
if err != nil {
log.Fatal(err)
}
r.Use(mw)
Cache-Control Middleware ¶
CacheControlMiddleware sets Cache-Control and Expires response headers based on the response Content-Type. Rules are evaluated in order; the first rule whose ContentType prefix matches wins. If no rule matches and DefaultValue/DefaultExpires is non-empty, it is used. When the handler already sets a Cache-Control or Expires header, the middleware does not overwrite the respective header.
mw, err := muxhandlers.CacheControlMiddleware(muxhandlers.CacheControlConfig{
Rules: []muxhandlers.CacheControlRule{
{ContentType: "image/", Value: "public, max-age=86400", Expires: 24 * time.Hour},
{ContentType: "application/json", Value: "no-cache", Expires: 0},
},
DefaultValue: "no-store",
DefaultExpires: 0,
})
if err != nil {
log.Fatal(err)
}
r.Use(mw)
Static Files Handler ¶
StaticFilesHandler serves static files from any fs.FS implementation (os.DirFS, embed.FS, fstest.MapFS, etc.) using http.FileServerFS. It is not middleware — it returns an http.Handler that serves files directly. Directory listing is disabled by default; when a directory has no index.html, a 404 is returned instead of a file listing. When SPAFallback is enabled, requests for non-existent paths serve the root index.html, allowing client-side routers to handle routing. PathPrefix strips the URL prefix internally, replacing http.StripPrefix. Aliases map custom URL paths to specific files in the FS.
handler, err := muxhandlers.StaticFilesHandler(muxhandlers.StaticFilesConfig{
FS: staticFS,
PathPrefix: "/ui",
EnableETag: true,
Aliases: map[string]string{
"/policy-builder/": "policy-builder.html",
"/policy-playground/": "policy-playground.html",
},
})
if err != nil {
log.Fatal(err)
}
r.PathPrefix("/ui/").Handler(handler)
Profiler Handler ¶
RegisterProfiler registers the standard net/http/pprof and expvar endpoints on the given router. It is not middleware — it registers routes directly. Endpoints use the standard /debug/pprof/ and /debug/vars paths. Mount with any prefix using Route:
r.Route("/_internal", muxhandlers.RegisterProfiler)
// serves /_internal/debug/pprof/, /_internal/debug/vars, etc.
Sunset Middleware ¶
SunsetMiddleware sets the Sunset response header per RFC 8594 to indicate that a resource will become unresponsive at a specific point in time. Optionally sets the Deprecation header and a Link header with rel="sunset".
mw, err := muxhandlers.SunsetMiddleware(muxhandlers.SunsetConfig{
Sunset: time.Date(2025, 12, 31, 23, 59, 59, 0, time.UTC),
Deprecation: time.Date(2025, 6, 1, 0, 0, 0, 0, time.UTC),
Link: "https://example.com/docs/migration",
})
if err != nil {
log.Fatal(err)
}
r.Use(mw)
Idempotency Middleware ¶
IdempotencyMiddleware caches responses keyed by the Idempotency-Key header per draft-ietf-httpapi-idempotency-key-header. Duplicate requests with the same key replay the cached response without invoking the handler. The middleware requires an IdempotencyStore implementation for persistence (e.g. Redis, in-memory).
mw, err := muxhandlers.IdempotencyMiddleware(muxhandlers.IdempotencyConfig{
Store: redisStore,
TTL: 1 * time.Hour,
})
if err != nil {
log.Fatal(err)
}
r.Use(mw)
Content Negotiation Middleware ¶
ContentNegotiationMiddleware performs proactive content negotiation per RFC 9110 Section 12.5.1. It parses the Accept header with quality values, selects the best matching type from the offered list, and stores the result in the request context. Use NegotiatedType to retrieve the selected type inside a handler. When Offered is empty, any media type is accepted. When no offered type matches, the middleware responds with 406 Not Acceptable.
r.Use(muxhandlers.ContentNegotiationMiddleware(muxhandlers.ContentNegotiationConfig{
Offered: []string{"application/json", "application/xml", "text/html"},
}))
Inside a handler:
func handler(w http.ResponseWriter, r *http.Request) {
switch muxhandlers.NegotiatedType(r) {
case "application/json":
mux.ResponseJSON(w, http.StatusOK, data)
case "application/xml":
mux.ResponseXML(w, http.StatusOK, data)
}
}
Problem Details ¶
WriteProblemDetails writes an RFC 9457 Problem Details JSON response with Content-Type "application/problem+json". The ProblemDetails struct contains the standard members (type, title, status, detail, instance) and supports extension members via the Extensions map. NewProblemDetails creates a ProblemDetails with the status code and standard status text as title.
muxhandlers.WriteProblemDetails(w, muxhandlers.ProblemDetails{
Type: "https://example.com/errors/not-found",
Title: "Resource not found",
Status: http.StatusNotFound,
Detail: "User with ID 42 was not found",
})
With extensions:
muxhandlers.WriteProblemDetails(w, muxhandlers.ProblemDetails{
Type: "https://example.com/errors/validation",
Title: "Validation Error",
Status: http.StatusUnprocessableEntity,
Extensions: map[string]any{
"errors": []string{"email is required"},
},
})
Quick error response:
muxhandlers.WriteProblemDetails(w, muxhandlers.NewProblemDetails(http.StatusForbidden))
Early Hints Middleware ¶
EarlyHintsMiddleware sends a 103 Early Hints informational response per RFC 8297 before the final response. This allows clients to begin preloading resources (stylesheets, scripts, fonts) while the server is still processing the request. The configured Link headers are sent with the 103 response and are not carried over to the final response.
mw, err := muxhandlers.EarlyHintsMiddleware(muxhandlers.EarlyHintsConfig{
Links: []string{
`</style.css>; rel=preload; as=style`,
`</app.js>; rel=preload; as=script`,
},
})
if err != nil {
log.Fatal(err)
}
r.Use(mw)
Redirect Middleware ¶
RedirectMiddleware redirects requests based on path matching rules. It supports exact path matching and prefix matching with a trailing wildcard ("*"). The first matching rule wins. Non-matching requests pass through. The redirect response includes a Location header and an HTML body with a <meta http-equiv="refresh"> tag for clients that do not follow the Location header automatically.
mw, err := muxhandlers.RedirectMiddleware(muxhandlers.RedirectConfig{
Rules: []muxhandlers.RedirectRule{
{From: "/", To: "/swagger/"},
{From: "/old-page", To: "/new-page"},
{From: "/blog/2023/*", To: "/archive/2023/"},
{From: "/github", To: "https://github.com/example", StatusCode: http.StatusTemporaryRedirect},
},
})
if err != nil {
log.Fatal(err)
}
r.Use(mw)
IP Allow Middleware ¶
IPAllowMiddleware restricts access to requests originating from a configured set of IP addresses and CIDR ranges. Requests from IPs not in the allowed list are rejected with 403 Forbidden by default. Use DeniedHandler to customize the error response.
mw, err := muxhandlers.IPAllowMiddleware(muxhandlers.IPAllowConfig{
Allowed: []string{"10.0.0.0/8", "192.168.0.0/16"},
})
if err != nil {
log.Fatal(err)
}
r.Use(mw)
Access Log Middleware ¶
AccessLogMiddleware records a structured entry for every request, capturing the response status code and byte count via a wrapped http.ResponseWriter. By default entries are emitted through log/slog (slog.Default when no Logger is provided). The Logger field accepts a fully pre-configured parent *slog.Logger: the middleware inherits its handler, output, format, level, and pre-bound attributes (from Logger.With or Logger.WithGroup), and appends per-request fields to every emitted record. Set LogFunc to bypass slog entirely and route entries to a custom sink.
5xx responses are logged at Error level; otherwise-Info requests are escalated to Warn when their duration exceeds SlowThreshold. Use Skip to suppress logging for health checks or metrics endpoints. Header capture is opt-in via IncludeHeaders, and Authorization, Cookie, Proxy-Authorization, and Set-Cookie are always redacted when captured.
r := mux.NewRouter()
r.Use(muxhandlers.AccessLogMiddleware(r, muxhandlers.AccessLogConfig{
SlowThreshold: 500 * time.Millisecond,
Skip: func(router *mux.Router, req *http.Request) bool {
return req.URL.Path == "/healthz"
},
}))
Graceful Shutdown Middleware ¶
GracefulShutdownMiddleware intercepts new requests once Drain has been called and a Drainer is the control surface returned alongside the middleware. Requests arriving before Drain() flow through unchanged and are counted in Drainer.InFlight; requests arriving after Drain() receive a 503 with Connection: close (RFC 9110 Sections 15.6.4 and 7.6.1 respectively) so keep-alive clients reconnect to a healthy peer. Bypass forwards selected requests (typically /healthz, /readyz, /metrics) so the orchestrator can observe the drain. Drainer.Wait blocks until in-flight requests have completed or the supplied context fires.
mw, drainer := muxhandlers.GracefulShutdownMiddleware(r, muxhandlers.GracefulShutdownConfig{
RetryAfter: 15 * time.Second,
Bypass: func(_ *mux.Router, req *http.Request) bool {
return req.URL.Path == "/healthz" || req.URL.Path == "/readyz"
},
})
r.Use(mw)
stop := make(chan os.Signal, 1)
signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
<-stop
drainer.Drain()
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_ = drainer.Wait(ctx)
_ = srv.Shutdown(ctx)
Maintenance Mode Middleware ¶
MaintenanceModeMiddleware short-circuits matching requests with a 503 Service Unavailable response (RFC 9110 Section 15.6.4) while a maintenance window is active. The Enabled predicate is the single source of truth; callers back it with whatever they like (atomic.Bool, file presence, env var, cron window) and the middleware reads it per request. Bypass lets specific requests through during maintenance (admin IPs, deploy tooling, health checks). Response, when set, fully owns the response body so the caller can render an HTML maintenance page, return RFC 9457 ProblemDetails JSON, or redirect to a static page. RetryAfter / RetryAt populate the Retry-After header in either delta-seconds or HTTP-date form.
var inMaintenance atomic.Bool
r.Use(muxhandlers.MaintenanceModeMiddleware(r, muxhandlers.MaintenanceConfig{
Enabled: func(_ *http.Request) bool { return inMaintenance.Load() },
RetryAfter: 5 * time.Minute,
Bypass: func(_ *mux.Router, req *http.Request) bool {
return req.Header.Get("X-Admin-Token") == adminToken
},
}))
No-Cache Middleware ¶
NoCacheMiddleware forces responses to be uncacheable. It rewrites caching headers on the response writer at the moment the handler flushes its status line, overriding any Cache-Control, Pragma, or Expires the handler may have set, and removes ETag and Last-Modified so downstream caches cannot perform conditional revalidation. The Modern preset emits Cache-Control: no-store per RFC 9111 Section 5.2.2.5; Strict adds the legacy Pragma and Expires combo for HTTP/1.0-era intermediaries.
r := mux.NewRouter()
r.Use(muxhandlers.NoCacheMiddleware(r, muxhandlers.NoCacheConfig{
Preset: muxhandlers.NoCachePresetStrict,
Skip: func(_ *mux.Router, req *http.Request) bool {
return strings.HasPrefix(req.URL.Path, "/assets/")
},
}))
HTCPCP Middleware ¶
HTCPCPMiddleware implements the Hyper Text Coffee Pot Control Protocol (RFC 2324) extended for tea efflux appliances (RFC 7168). It intercepts BREW and WHEN requests and responds according to the configured pot type. A teapot asked to brew coffee returns 418 I'm a Teapot; a coffee pot asked for tea returns 406 Not Acceptable; an empty pot returns 503 Service Unavailable with Retry-After. Non-HTCPCP methods pass through unchanged.
By default the middleware only activates on April 1 (the publication date of both RFCs); on every other day it becomes a no-op. Override ActiveOn to force-enable the protocol or restrict it further.
r.Route("/pot", func(pot *mux.Router) {
pot.Use(muxhandlers.HTCPCPMiddleware(muxhandlers.HTCPCPConfig{
PotType: muxhandlers.PotTeapot,
Teas: []string{"earl-grey", "rooibos"},
}))
pot.HandleFunc("/", potStatusHandler)
})
Index ¶
- Constants
- Variables
- func AcceptPatchMiddleware(router *mux.Router, cfg AcceptPatchConfig) mux.MiddlewareFunc
- func AccessLogMiddleware(router *mux.Router, cfg AccessLogConfig) mux.MiddlewareFunc
- func BasicAuthMiddleware(cfg BasicAuthConfig) (mux.MiddlewareFunc, error)
- func BearerAuthMiddleware(cfg BearerAuthConfig) (mux.MiddlewareFunc, error)
- func CORSMiddleware(r *mux.Router, cfg CORSConfig) (mux.MiddlewareFunc, error)
- func CacheControlMiddleware(cfg CacheControlConfig) (mux.MiddlewareFunc, error)
- func CanonicalHostMiddleware(cfg CanonicalHostConfig) (mux.MiddlewareFunc, error)
- func CompressionMiddleware(cfg CompressionConfig) (mux.MiddlewareFunc, error)
- func ContentNegotiationMiddleware(cfg ContentNegotiationConfig) mux.MiddlewareFunc
- func ContentTypeCheckMiddleware(cfg ContentTypeCheckConfig) (mux.MiddlewareFunc, error)
- func EarlyHintsMiddleware(cfg EarlyHintsConfig) (mux.MiddlewareFunc, error)
- func GenerateUUIDv4(_ *http.Request) string
- func GenerateUUIDv7(_ *http.Request) string
- func HTCPCPMiddleware(cfg HTCPCPConfig) mux.MiddlewareFunc
- func IPAllowMiddleware(cfg IPAllowConfig) (mux.MiddlewareFunc, error)
- func IdempotencyMiddleware(cfg IdempotencyConfig) (mux.MiddlewareFunc, error)
- func IsAprilFirst(t time.Time) bool
- func MaintenanceModeMiddleware(router *mux.Router, cfg MaintenanceConfig) mux.MiddlewareFunc
- func MethodOverrideMiddleware(cfg MethodOverrideConfig) (mux.MiddlewareFunc, error)
- func NegotiatedType(r *http.Request) string
- func NoCacheMiddleware(router *mux.Router, cfg NoCacheConfig) mux.MiddlewareFunc
- func PatchContentType(r *http.Request) string
- func PatchRoutingMiddleware(cfg PatchRoutingConfig) mux.MiddlewareFunc
- func ProxyHeadersMiddleware(cfg ProxyHeadersConfig) (mux.MiddlewareFunc, error)
- func RecoveryMiddleware(cfg RecoveryConfig) mux.MiddlewareFunc
- func RedirectMiddleware(cfg RedirectConfig) (mux.MiddlewareFunc, error)
- func RegisterProfiler(r *mux.Router)
- func RequestIDFromContext(ctx context.Context) string
- func RequestIDMiddleware(cfg RequestIDConfig) mux.MiddlewareFunc
- func RequestSizeLimitMiddleware(cfg RequestSizeLimitConfig) (mux.MiddlewareFunc, error)
- func SecurityHeadersMiddleware(cfg SecurityHeadersConfig) (mux.MiddlewareFunc, error)
- func ServerMiddleware(cfg ServerConfig) (mux.MiddlewareFunc, error)
- func StaticFilesHandler(cfg StaticFilesConfig) (http.Handler, error)
- func SunsetMiddleware(cfg SunsetConfig) (mux.MiddlewareFunc, error)
- func TimeoutMiddleware(cfg TimeoutConfig) (mux.MiddlewareFunc, error)
- func WriteProblemDetails(w http.ResponseWriter, problem ProblemDetails)
- type AcceptPatchConfig
- type AccessLogConfig
- type AccessLogEntry
- type BasicAuthConfig
- type BearerAuthConfig
- type CORSConfig
- type CacheControlConfig
- type CacheControlRule
- type CanonicalHostConfig
- type CompressionConfig
- type ContentNegotiationConfig
- type ContentTypeCheckConfig
- type Drainer
- type EarlyHintsConfig
- type GracefulShutdownConfig
- type HTCPCPConfig
- type IPAllowConfig
- type IdempotencyConfig
- type IdempotencyLocker
- type IdempotencyStore
- type MaintenanceConfig
- type MethodOverrideConfig
- type NoCacheConfig
- type NoCachePreset
- type PatchRoutingConfig
- type PotType
- type ProblemDetails
- type ProxyHeadersConfig
- type RecoveryConfig
- type RedirectConfig
- type RedirectRule
- type RequestIDConfig
- type RequestSizeLimitConfig
- type SecurityHeadersConfig
- type ServerConfig
- type StaticFilesConfig
- type SunsetConfig
- type TimeoutConfig
Constants ¶
const ( MethodBrew = "BREW" MethodWhen = "WHEN" )
HTCPCP method tokens defined by RFC 2324 Section 2.1.1 and extended for tea by RFC 7168 Section 2.1.1.
const ( // PatchTypeJSON is the implicit merge patch using standard JSON. PatchTypeJSON = "application/json" // PatchTypeMergePatch is the JSON Merge Patch format per RFC 7396. PatchTypeMergePatch = "application/merge-patch+json" // PatchTypeJSONPatch is the JSON Patch format per RFC 6902. PatchTypeJSONPatch = "application/json-patch+json" )
Patch content type constants for the supported PATCH formats.
const ContentTypeMessageCoffeePot = "message/coffeepot"
ContentTypeMessageCoffeePot is the message/coffeepot media type returned by a coffee pot to a successful BREW (RFC 2324 Section 4).
const ContentTypeMessageTeapot = "message/teapot"
ContentTypeMessageTeapot is the message/teapot media type returned by a teapot to a successful BREW (RFC 7168 Section 2.3.1).
const IdempotencySkipMetadataKey = "idempotency:skip"
IdempotencySkipMetadataKey is the route metadata key used to skip idempotency processing for specific routes. Set this key to true in route metadata to bypass the middleware.
r.HandleFunc("/health", handler).Metadata(muxhandlers.IdempotencySkipMetadataKey, true)
const StatusImATeapot = http.StatusTeapot
StatusImATeapot is the HTCPCP status code returned when a teapot is asked to brew coffee (RFC 2324 Section 2.3.2, RFC 7168 Section 2.3.3). Mirrors http.StatusTeapot from the standard library for callers that prefer the protocol-native name.
Variables ¶
var DefaultTeaVarieties = []string{
"black",
"chai",
"earl-grey",
"english-breakfast",
"green",
"jasmine",
"oolong",
"peppermint",
"rooibos",
}
DefaultTeaVarieties is the tea variety registry from RFC 7168 Section 2.1.1, used when HTCPCPConfig.Teas is nil for a teapot.
var DefaultTrustedProxies = []string{
"127.0.0.0/8",
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
"100.64.0.0/10",
"::1/128",
"fc00::/7",
}
DefaultTrustedProxies is the set of private and loopback ranges used when ProxyHeadersConfig.TrustedProxies is empty.
Included ranges:
- 127.0.0.0/8 — IPv4 loopback (RFC 1122)
- 10.0.0.0/8 — Class A private (RFC 1918)
- 172.16.0.0/12 — Class B private (RFC 1918)
- 192.168.0.0/16 — Class C private (RFC 1918)
- 100.64.0.0/10 — CGNAT shared address space (RFC 6598)
- ::1/128 — IPv6 loopback (RFC 4291)
- fc00::/7 — IPv6 unique local (RFC 4193)
var ErrCanonicalHostEmpty = errors.New("canonical host: URL must not be empty")
ErrCanonicalHostEmpty is returned when CanonicalHostConfig.URL is empty.
var ErrCanonicalHostInvalid = errors.New("canonical host: URL is not valid")
ErrCanonicalHostInvalid is returned when CanonicalHostConfig.URL cannot be parsed.
var ErrIPAllowEmpty = errors.New("ip allow: allowed list must not be empty")
ErrIPAllowEmpty is returned when IPAllowConfig.Allowed is empty.
var ErrIPAllowInvalidEntry = errors.New("ip allow: invalid entry")
ErrIPAllowInvalidEntry is returned when an Allowed entry is neither a valid IP address nor a valid CIDR range.
var ErrInvalidCompressionLevel = errors.New("compression: invalid compression level")
ErrInvalidCompressionLevel is returned when CompressionConfig.Level is outside the valid compression level range.
var ErrInvalidFrameOption = errors.New("security headers: frame option must be DENY, SAMEORIGIN, or empty")
ErrInvalidFrameOption is returned when SecurityHeadersConfig.FrameOption is not one of the valid values: "DENY", "SAMEORIGIN", or empty string.
var ErrInvalidMaxSize = errors.New("request size limit: max size must be greater than zero")
ErrInvalidMaxSize is returned when RequestSizeLimitConfig.MaxBytes is not greater than zero.
var ErrInvalidOverrideMethod = errors.New("method override: allowed methods must be valid HTTP methods")
ErrInvalidOverrideMethod is returned when MethodOverrideConfig.AllowedMethods or MethodOverrideConfig.OriginalMethods contains an invalid HTTP method.
var ErrInvalidProxy = errors.New("proxy headers: invalid proxy entry")
ErrInvalidProxy is returned when a TrustedProxies entry is neither a valid IP address nor a valid CIDR range.
var ErrInvalidTimeout = errors.New("timeout: duration must be greater than zero")
ErrInvalidTimeout is returned when TimeoutConfig.Duration is not greater than zero.
var ErrNoAllowedTypes = errors.New("content type check: at least one allowed content type is required")
ErrNoAllowedTypes is returned when ContentTypeCheckConfig.AllowedTypes is empty.
var ErrNoAuthSource = errors.New("basic auth: at least one of ValidateFunc or Credentials must be set")
ErrNoAuthSource is returned when BasicAuthConfig has neither ValidateFunc nor Credentials configured.
var ErrNoCacheControlRules = errors.New("cache control: at least one rule is required")
ErrNoCacheControlRules is returned when CacheControlConfig.Rules is empty.
var ErrNoIdempotencyStore = errors.New("idempotency: Store must be set")
ErrNoIdempotencyStore is returned when IdempotencyConfig.Store is nil.
var ErrNoLinks = errors.New("early hints: at least one Link must be set")
ErrNoLinks is returned when EarlyHintsConfig has neither Links nor LinksFunc configured.
var ErrNoTokenValidator = errors.New("bearer auth: ValidateFunc must be set")
ErrNoTokenValidator is returned when BearerAuthConfig has no ValidateFunc configured.
var ErrRedirectEmptyFrom = errors.New("redirect: rule From must not be empty")
ErrRedirectEmptyFrom is returned when a RedirectRule has an empty From field.
var ErrRedirectEmptyTo = errors.New("redirect: rule To must not be empty")
ErrRedirectEmptyTo is returned when a RedirectRule has an empty To field.
var ErrRedirectFromNoSlash = errors.New("redirect: rule From must start with /")
ErrRedirectFromNoSlash is returned when a RedirectRule.From does not start with "/".
var ErrRedirectNoRules = errors.New("redirect: rules must not be empty")
ErrRedirectNoRules is returned when RedirectConfig.Rules is empty.
var ErrStaticFilesAliasTargetNotFound = errors.New("static files: alias target file not found")
ErrStaticFilesAliasTargetNotFound is returned when an alias target file does not exist in the file system.
var ErrStaticFilesNoFS = errors.New("static files: file system must not be nil")
ErrStaticFilesNoFS is returned when StaticFilesConfig.FS is nil.
var ErrStaticFilesNoIndexHTML = errors.New("static files: index.html is required when SPA fallback is enabled")
ErrStaticFilesNoIndexHTML is returned when SPAFallback is enabled but the file system does not contain an index.html at the root.
var ErrSunsetZeroTime = errors.New("sunset: sunset time must not be zero")
ErrSunsetZeroTime is returned when SunsetConfig.Sunset is the zero time.
var ErrWildcardCredentials = errors.New("wildcard origin \"*\" cannot be used with AllowCredentials; use AllowOriginFunc instead")
ErrWildcardCredentials is returned when AllowedOrigins contains "*" and AllowCredentials is true. Use AllowOriginFunc for dynamic origin checks with credentials.
Functions ¶
func AcceptPatchMiddleware ¶ added in v0.7.0
func AcceptPatchMiddleware(router *mux.Router, cfg AcceptPatchConfig) mux.MiddlewareFunc
AcceptPatchMiddleware returns a middleware that handles OPTIONS requests by responding with Allow and Accept-Patch headers per RFC 5789. The Allow header is auto-discovered from the router's registered methods for the matched path. Non-OPTIONS requests pass through unchanged.
Because the router middleware only runs for matched routes, this function also sets the router's MethodNotAllowedHandler to intercept OPTIONS requests that would otherwise receive a 405 response.
Spec reference: https://www.rfc-editor.org/rfc/rfc5789#section-3.1
func AccessLogMiddleware ¶ added in v0.15.0
func AccessLogMiddleware(router *mux.Router, cfg AccessLogConfig) mux.MiddlewareFunc
AccessLogMiddleware records a structured entry for every request. The middleware wraps the response writer to capture the status code and response body byte count, runs the next handler, then emits an AccessLogEntry to LogFunc (when set) or to Logger (a slog logger). When neither is set, slog.Default() receives the entry.
5xx responses are logged at slog.LevelError; SlowThreshold escalates otherwise-Info requests to slog.LevelWarn when the handler runs longer than the threshold. Header capture is opt-in via IncludeHeaders; sensitive headers (Authorization, Cookie, Proxy-Authorization, Set-Cookie, plus anything in RedactHeaders) are always replaced by "[REDACTED]" when captured.
The router is accepted so the Skip predicate can resolve route metadata or use route-aware filtering. Pass the same *mux.Router the middleware is attached to via Use.
func BasicAuthMiddleware ¶
func BasicAuthMiddleware(cfg BasicAuthConfig) (mux.MiddlewareFunc, error)
BasicAuthMiddleware returns a middleware that implements HTTP Basic Authentication per RFC 7617. It validates the Authorization header and responds with 401 Unauthorized when credentials are missing or invalid.
It returns ErrNoAuthSource if both ValidateFunc and Credentials are nil/empty.
func BearerAuthMiddleware ¶ added in v0.7.0
func BearerAuthMiddleware(cfg BearerAuthConfig) (mux.MiddlewareFunc, error)
BearerAuthMiddleware returns a middleware that implements HTTP Bearer Token Authentication per RFC 6750. It extracts the token from the Authorization header and validates it using the configured ValidateFunc.
When the Authorization header is missing, malformed, or the token is invalid, the middleware responds with 401 Unauthorized and a WWW-Authenticate: Bearer header per RFC 6750 Section 3.
It returns ErrNoTokenValidator if ValidateFunc is nil.
func CORSMiddleware ¶
func CORSMiddleware(r *mux.Router, cfg CORSConfig) (mux.MiddlewareFunc, error)
CORSMiddleware returns a middleware that implements the full CORS protocol per the Fetch Standard (https://fetch.spec.whatwg.org/#http-cors-protocol). It validates the Origin header (RFC 6454), handles preflight OPTIONS requests, and sets the appropriate response headers.
It returns an error if the configuration is invalid (e.g. wildcard origin combined with AllowCredentials).
Because the router middleware only runs for matched routes, this function also sets the router's MethodNotAllowedHandler to intercept CORS preflight OPTIONS requests that would otherwise receive a 405 response.
func CacheControlMiddleware ¶ added in v0.2.0
func CacheControlMiddleware(cfg CacheControlConfig) (mux.MiddlewareFunc, error)
CacheControlMiddleware returns a middleware that sets Cache-Control and Expires response headers based on the response Content-Type. Rules are evaluated in order; the first rule whose ContentType prefix matches wins. If no rule matches and DefaultValue/DefaultExpires is non-empty, it is used. When the handler already sets a Cache-Control or Expires header, the middleware does not overwrite the respective header.
It returns ErrNoCacheControlRules if Rules is empty.
func CanonicalHostMiddleware ¶ added in v0.7.0
func CanonicalHostMiddleware(cfg CanonicalHostConfig) (mux.MiddlewareFunc, error)
CanonicalHostMiddleware returns a middleware that redirects requests to the canonical host when the incoming request scheme or host does not match. The request path and query string are preserved.
This is useful for enforcing a single canonical URL (e.g. redirecting example.com to www.example.com, or HTTP to HTTPS).
func CompressionMiddleware ¶ added in v0.2.0
func CompressionMiddleware(cfg CompressionConfig) (mux.MiddlewareFunc, error)
CompressionMiddleware returns a middleware that compresses response bodies using gzip or deflate when the client advertises support via the Accept-Encoding header. Gzip is preferred over deflate when the client accepts both. It uses sync.Pool instances to reuse writers for performance.
Compression is skipped when:
- The request does not include "gzip" or "deflate" in Accept-Encoding
- The response already has a Content-Encoding header
- The response Content-Type is an inherently compressed format (image/*, video/*, audio/*, or common archive types)
It returns ErrInvalidCompressionLevel if Level is outside the valid range.
func ContentNegotiationMiddleware ¶ added in v0.7.0
func ContentNegotiationMiddleware(cfg ContentNegotiationConfig) mux.MiddlewareFunc
ContentNegotiationMiddleware returns a middleware that performs proactive content negotiation per RFC 9110 Section 12.5.1. It parses the Accept header, selects the best matching type from the offered list, and stores the result in the request context (retrievable via NegotiatedType).
When Offered is empty, any media type is accepted: the highest quality type from the Accept header is stored in context, and requests always pass through.
When the Accept header is absent or empty, the first offered type is selected per RFC 9110 Section 12.5.1 ("A request without any Accept header field implies that the user agent will accept any media type").
When no offered type matches the Accept header, the middleware responds with 406 Not Acceptable per RFC 9110 Section 15.5.7.
func ContentTypeCheckMiddleware ¶ added in v0.2.0
func ContentTypeCheckMiddleware(cfg ContentTypeCheckConfig) (mux.MiddlewareFunc, error)
ContentTypeCheckMiddleware returns a middleware that validates the Content-Type header on requests with matching methods. It returns 415 Unsupported Media Type when the Content-Type is missing or does not match any of the allowed types.
It returns ErrNoAllowedTypes if AllowedTypes is empty.
func EarlyHintsMiddleware ¶ added in v0.7.0
func EarlyHintsMiddleware(cfg EarlyHintsConfig) (mux.MiddlewareFunc, error)
EarlyHintsMiddleware returns a middleware that sends a 103 Early Hints informational response per RFC 8297 before the final response. This allows clients to begin preloading resources (stylesheets, scripts, fonts) while the server is still processing the request.
The middleware sets the configured Link headers and writes a 103 status code. The downstream handler then writes the final response as usual. Link headers from the 103 response are not carried over to the final response.
It returns ErrNoLinks if both Links is empty and LinksFunc is nil.
func GenerateUUIDv4 ¶ added in v0.2.0
GenerateUUIDv4 returns a new UUID v4 string.
Spec reference: https://www.rfc-editor.org/rfc/rfc9562#section-5.4
func GenerateUUIDv7 ¶ added in v0.2.0
GenerateUUIDv7 returns a new UUID v7 string. UUIDs are time-ordered: IDs generated later sort lexicographically after earlier ones.
Spec reference: https://www.rfc-editor.org/rfc/rfc9562#section-5.7
func HTCPCPMiddleware ¶ added in v0.15.0
func HTCPCPMiddleware(cfg HTCPCPConfig) mux.MiddlewareFunc
HTCPCPMiddleware implements the Hyper Text Coffee Pot Control Protocol (RFC 2324) extended for tea (RFC 7168). It intercepts BREW and WHEN requests and responds according to the configured pot type. All other methods pass through to the next handler.
Behavior summary:
- Teapot receiving BREW with a coffee variety: 418 I'm a Teapot (RFC 7168 Section 2.3.3).
- Teapot receiving BREW with a supported tea variety: 200 OK with Content-Type: message/teapot.
- Coffee pot receiving BREW with a tea variety: 406 Not Acceptable.
- Either pot receiving BREW while Empty is true: 503 Service Unavailable + Retry-After.
- WHEN: 200 OK acknowledging the pour stop (RFC 2324 Section 2.1.2).
- BREW asking for an addition not listed in AvailableAdditions: 406 Not Acceptable.
func IPAllowMiddleware ¶ added in v0.6.0
func IPAllowMiddleware(cfg IPAllowConfig) (mux.MiddlewareFunc, error)
IPAllowMiddleware returns a middleware that restricts access to requests originating from the configured IP addresses and CIDR ranges. Requests from IPs not in the allowed list are rejected. The client IP is extracted from r.RemoteAddr.
func IdempotencyMiddleware ¶ added in v0.7.0
func IdempotencyMiddleware(cfg IdempotencyConfig) (mux.MiddlewareFunc, error)
IdempotencyMiddleware returns a middleware that caches responses keyed by the Idempotency-Key header per draft-ietf-httpapi-idempotency-key-header. When a request carries a key that has been seen before, the cached response is replayed without invoking the downstream handler. The cached response includes the Idempotency-Key header in the replay.
It returns ErrNoIdempotencyStore if Store is nil.
func IsAprilFirst ¶ added in v0.15.0
IsAprilFirst reports whether the given instant falls on April 1 in its location. Used as the default ActiveOn predicate so HTCPCP-TEA (RFC 2324 published 1 April 1998, extended by RFC 7168 on 1 April 2014) only activates on its anniversary.
func MaintenanceModeMiddleware ¶ added in v0.15.0
func MaintenanceModeMiddleware(router *mux.Router, cfg MaintenanceConfig) mux.MiddlewareFunc
MaintenanceModeMiddleware short-circuits matching requests with a "service unavailable" response while a maintenance window is active. The Enabled predicate is the single source of truth; callers back it with whatever they like (atomic.Bool, file presence, env var, cron window) and the middleware reads it per request.
When Enabled returns true and Bypass does not, the middleware sets Retry-After (if configured) and either invokes Response, when set, or writes a default plain-text body with StatusCode. When Enabled returns false, or Bypass returns true, the request flows through to the next handler unchanged.
The router is accepted so the Bypass predicate can resolve route metadata. Pass the same *mux.Router the middleware is attached to via Use.
func MethodOverrideMiddleware ¶ added in v0.2.0
func MethodOverrideMiddleware(cfg MethodOverrideConfig) (mux.MiddlewareFunc, error)
MethodOverrideMiddleware returns a middleware that allows clients to override the HTTP method via a configurable header. The first non-empty header value from HeaderNames is uppercased and checked against the allowed set. When allowed, r.Method is set to the override value and the header is removed. Override is only applied when the original request method is in OriginalMethods (defaults to POST).
It returns ErrInvalidOverrideMethod if AllowedMethods or OriginalMethods contains an invalid method.
func NegotiatedType ¶ added in v0.7.0
NegotiatedType returns the content type selected by ContentNegotiationMiddleware from the request context. Returns an empty string if no negotiation was performed.
func NoCacheMiddleware ¶ added in v0.15.0
func NoCacheMiddleware(router *mux.Router, cfg NoCacheConfig) mux.MiddlewareFunc
NoCacheMiddleware forces responses to be uncacheable. It rewrites caching headers on the response writer at the moment the handler flushes its status line, overriding any Cache-Control, Pragma, or Expires the handler may have set, and removes ETag and Last-Modified so downstream caches cannot perform conditional revalidation.
The Modern preset emits Cache-Control: no-store per RFC 9111 Section 5.2.2.5; Strict adds the legacy Pragma and Expires header combo expected by HTTP/1.0-era intermediaries.
The router argument is accepted so the Skip predicate can resolve route metadata. Pass the same *mux.Router the middleware is attached to via Use.
func PatchContentType ¶ added in v0.7.0
PatchContentType returns the patch content type stored in the request context by PatchRoutingMiddleware. Returns an empty string for non-PATCH requests or when the middleware is not applied.
func PatchRoutingMiddleware ¶ added in v0.7.0
func PatchRoutingMiddleware(cfg PatchRoutingConfig) mux.MiddlewareFunc
PatchRoutingMiddleware returns a middleware that validates the Content-Type of PATCH requests against a set of allowed patch formats and stores the resolved type in the request context. Non-PATCH requests pass through unchanged.
The resolved type is retrievable via PatchContentType.
Returns 415 Unsupported Media Type when the Content-Type is missing or does not match any allowed type.
Spec references:
- https://www.rfc-editor.org/rfc/rfc7396 (JSON Merge Patch)
- https://www.rfc-editor.org/rfc/rfc6902 (JSON Patch)
func ProxyHeadersMiddleware ¶
func ProxyHeadersMiddleware(cfg ProxyHeadersConfig) (mux.MiddlewareFunc, error)
ProxyHeadersMiddleware returns a middleware that populates request fields from reverse proxy headers when the request originates from a trusted proxy.
Supported headers (checked in priority order):
- r.RemoteAddr: X-Forwarded-For > X-Real-IP [> Forwarded for=]
- r.URL.Scheme: X-Forwarded-Proto > X-Forwarded-Scheme [> Forwarded proto=]
- r.Host: X-Forwarded-Host [> Forwarded host=]
- X-Forwarded-By header: [Forwarded by=]
Bracketed entries require EnableForwarded (RFC 7239). The by= directive is exposed as a synthetic X-Forwarded-By request header.
When TrustedProxies is empty, DefaultTrustedProxies (private RFC 1918/4193 and loopback ranges) is used.
It returns an error if the configuration contains unparseable IP/CIDR entries.
func RecoveryMiddleware ¶ added in v0.2.0
func RecoveryMiddleware(cfg RecoveryConfig) mux.MiddlewareFunc
RecoveryMiddleware returns a middleware that recovers from panics in downstream handlers. When a panic occurs it returns 500 Internal Server Error to the client and optionally invokes LogFunc.
func RedirectMiddleware ¶ added in v0.8.0
func RedirectMiddleware(cfg RedirectConfig) (mux.MiddlewareFunc, error)
RedirectMiddleware returns a middleware that redirects requests based on path matching rules. It supports exact path matching and prefix matching with a trailing wildcard ("*"). Non-matching requests are passed through to the next handler.
The redirect response includes a standard Location header and an HTML body with a <meta http-equiv="refresh"> tag for clients that do not follow the Location header automatically.
func RegisterProfiler ¶ added in v0.8.1
RegisterProfiler registers the standard net/http/pprof and expvar endpoints on the given router. Mount using Route or PathPrefix:
r.Route("/debug", muxhandlers.RegisterProfiler)
r.Route("/_internal", muxhandlers.RegisterProfiler)
Registered endpoints (relative to the mount path):
/debug/pprof/ - pprof index page /debug/pprof/cmdline - running program command line /debug/pprof/profile - CPU profile (supports ?seconds=N) /debug/pprof/symbol - symbol lookup /debug/pprof/trace - execution trace (supports ?seconds=N) /debug/vars - exported variables via the expvar package
Named profiles (allocs, block, goroutine, heap, mutex, threadcreate) are served by the index handler.
See: https://pkg.go.dev/net/http/pprof See: https://pkg.go.dev/expvar
func RequestIDFromContext ¶ added in v0.2.0
RequestIDFromContext returns the request ID stored in the context by RequestIDMiddleware. Returns an empty string if no ID is present.
func RequestIDMiddleware ¶ added in v0.2.0
func RequestIDMiddleware(cfg RequestIDConfig) mux.MiddlewareFunc
RequestIDMiddleware returns a middleware that generates or propagates a request ID header. The ID is set on both the request (for downstream handlers) and the response (for the caller).
func RequestSizeLimitMiddleware ¶ added in v0.2.0
func RequestSizeLimitMiddleware(cfg RequestSizeLimitConfig) (mux.MiddlewareFunc, error)
RequestSizeLimitMiddleware returns a middleware that limits the size of incoming request bodies. It wraps r.Body with http.MaxBytesReader so that downstream handlers receive an error when reading beyond the limit. The standard http.MaxBytesReader returns 413 Request Entity Too Large automatically when the limit is exceeded.
It returns ErrInvalidMaxSize if MaxBytes is not greater than zero.
func SecurityHeadersMiddleware ¶ added in v0.2.0
func SecurityHeadersMiddleware(cfg SecurityHeadersConfig) (mux.MiddlewareFunc, error)
SecurityHeadersMiddleware returns a middleware that sets common security response headers. Headers are set before calling the next handler.
It returns ErrInvalidFrameOption if FrameOption is set to a value other than "DENY", "SAMEORIGIN", or empty string.
func ServerMiddleware ¶ added in v0.2.0
func ServerMiddleware(cfg ServerConfig) (mux.MiddlewareFunc, error)
ServerMiddleware returns a middleware that sets server identification response headers. The hostname is resolved once when the middleware is created. It returns an error if the hostname cannot be determined.
func StaticFilesHandler ¶ added in v0.2.0
func StaticFilesHandler(cfg StaticFilesConfig) (http.Handler, error)
StaticFilesHandler returns an http.Handler that serves static files from the provided file system. It is not middleware — it serves files directly without calling a next handler.
func SunsetMiddleware ¶ added in v0.6.0
func SunsetMiddleware(cfg SunsetConfig) (mux.MiddlewareFunc, error)
SunsetMiddleware returns a middleware that sets the Sunset response header per RFC 8594. Optionally sets the Deprecation and Link headers.
func TimeoutMiddleware ¶ added in v0.2.0
func TimeoutMiddleware(cfg TimeoutConfig) (mux.MiddlewareFunc, error)
TimeoutMiddleware returns a middleware that limits handler execution time. It wraps the handler with http.TimeoutHandler, which returns 503 Service Unavailable when the handler does not complete within the configured duration.
It returns ErrInvalidTimeout if Duration is not greater than zero.
func WriteProblemDetails ¶ added in v0.7.0
func WriteProblemDetails(w http.ResponseWriter, problem ProblemDetails)
WriteProblemDetails writes an RFC 9457 Problem Details JSON response. It sets Content-Type to "application/problem+json" and writes the status code from the ProblemDetails struct. If encoding fails, an HTTP 500 Internal Server Error is written instead.
Types ¶
type AcceptPatchConfig ¶ added in v0.7.0
type AcceptPatchConfig struct {
// AcceptPatchTypes is the list of Content-Type values advertised in
// the Accept-Patch response header for OPTIONS requests. When nil,
// defaults to application/json, application/merge-patch+json,
// and application/json-patch+json.
AcceptPatchTypes []string
// StatusCode is the HTTP status code for OPTIONS responses.
// Defaults to 204 No Content.
StatusCode int
}
AcceptPatchConfig configures the Accept-Patch middleware.
type AccessLogConfig ¶ added in v0.15.0
type AccessLogConfig struct {
// Logger is the slog logger used when LogFunc is nil. It may be a
// fully pre-configured logger: the middleware inherits whatever
// handler, output sink, format, level, and pre-bound attributes
// the caller has set (via slog.New, Logger.With, Logger.WithGroup,
// etc.). Per-request access-log fields are appended to every
// emitted record alongside those inherited attributes.
// Defaults to slog.Default() when both Logger and LogFunc are nil.
Logger *slog.Logger
// LogFunc, when non-nil, fully takes over emission: the middleware
// builds an AccessLogEntry and hands it to LogFunc instead of
// touching Logger. Use this to integrate with non-slog sinks or to
// suppress logging conditionally.
LogFunc func(*AccessLogEntry)
// Skip, when non-nil, is consulted before the handler runs; if it
// returns true the request is processed without any log emission.
// The first argument is the router this middleware was attached
// to, so callers can resolve the matched route or its metadata
// (e.g. mux.CurrentRoute(r).GetMetadataValueOr("skip_log", false))
// to decide. Use it to silence health checks, metrics scrapes, or
// routes tagged via metadata.
Skip func(*mux.Router, *http.Request) bool
// IncludeHeaders lists request header names to record into
// AccessLogEntry.Headers. Names are matched case-insensitively.
// When nil, no headers are captured.
IncludeHeaders []string
// RedactHeaders lists header names whose values are replaced with
// "[REDACTED]" in AccessLogEntry.Headers. Authorization, Cookie,
// Proxy-Authorization, and Set-Cookie are always redacted in
// addition to anything supplied here. Names are case-insensitive.
RedactHeaders []string
// SlowThreshold, when greater than zero, raises the slog level of
// requests whose duration exceeds it to Warn. Has no effect on
// LogFunc, which always receives the full entry regardless of
// duration.
SlowThreshold time.Duration
// Now overrides the clock source used for entry timestamps and
// duration measurement. Defaults to time.Now. Intended for tests.
Now func() time.Time
}
AccessLogConfig configures the AccessLog middleware.
type AccessLogEntry ¶ added in v0.15.0
type AccessLogEntry struct {
// Time is when the request started processing.
Time time.Time
// Method is the HTTP request method (RFC 9110 Section 9).
Method string
// Proto is the request protocol as reported by r.Proto, e.g.
// "HTTP/1.1", "HTTP/2.0".
Proto string
// Scheme is the resolved request scheme, "http" or "https". It
// uses mux.Scheme(r), which infers https when r.URL.Scheme is set
// (typically by ProxyHeadersMiddleware from a trusted
// X-Forwarded-Proto) or when the connection is TLS.
Scheme string
// Host is r.Host (the Host header value, post-proxy resolution
// when ProxyHeadersMiddleware is upstream). Useful for multi-vhost
// deployments.
Host string
// Path is r.URL.Path as observed at handler entry; it reflects any
// path normalization or rewriting performed by upstream middleware.
Path string
// Query is r.URL.RawQuery; empty when no query string was present.
Query string
// Status is the HTTP status code written by the handler. Defaults
// to 200 when the handler completed without calling WriteHeader,
// matching net/http behavior. Set to 0 when Hijacked is true,
// because the upgrader writes the response bytes directly to the
// hijacked connection and the middleware cannot observe them.
Status int
// Hijacked is true when the handler hijacked the connection via
// http.Hijacker. The status code is no longer observable by the
// middleware once a hijack succeeds (the upgrader writes raw bytes
// to the underlying net.Conn), so Status is zeroed and downstream
// consumers should treat the entry as "connection upgraded /
// handed off" rather than a normal 2xx/4xx/5xx response. For
// WebSocket upgrades the wire status is typically 101 Switching
// Protocols.
Hijacked bool
// Bytes is the total number of response body bytes written.
Bytes int64
// Duration is the wall-clock time spent in the handler chain.
Duration time.Duration
// RemoteAddr is r.RemoteAddr after any upstream proxy header
// resolution. Use ProxyHeadersMiddleware to populate this from
// trusted forwarded headers.
RemoteAddr string
// UserAgent is the request's User-Agent header value.
UserAgent string
// Referer is the request's Referer header value (RFC 9110 Section
// 10.1.3, "Referer" preserves the original misspelling).
Referer string
// RouteName is the name set via mux.Route.Name, when the matched
// route has one. Empty when the route is unnamed or no route was
// matched.
RouteName string
// RequestID is the value returned by RequestIDFromContext, when the
// request flowed through RequestIDMiddleware. Empty otherwise.
RequestID string
// Headers contains the request headers selected by IncludeHeaders,
// with values for headers in RedactHeaders replaced by "[REDACTED]".
// Nil when no headers are captured.
Headers map[string]string
// Err is set when ErrorFunc detects an application-level error
// (e.g. 5xx status). Optional and informational.
Err error
}
AccessLogEntry is the structured record produced for every request the AccessLog middleware observes. It is supplied to a user-provided callback (when LogFunc is set), and is also the source of fields the default slog sink emits.
type BasicAuthConfig ¶
type BasicAuthConfig struct {
// Realm is the authentication realm sent in the WWW-Authenticate header.
// Defaults to "Restricted" when empty.
Realm string
// ValidateFunc is called to validate credentials dynamically.
// Takes priority over Credentials when both are set.
ValidateFunc func(username, password string) bool
// Credentials is a static map of username -> password pairs.
// Compared using SHA-256 hashed constant-time comparison to prevent
// timing attacks, including length-based leaks.
Credentials map[string]string
}
BasicAuthConfig configures the Basic Auth middleware behaviour.
Spec reference: https://www.rfc-editor.org/rfc/rfc7617
type BearerAuthConfig ¶ added in v0.7.0
type BearerAuthConfig struct {
// Realm is the authentication realm sent in the WWW-Authenticate header.
// Defaults to "Restricted" when empty.
Realm string
// ValidateFunc is called to validate the bearer token.
// It receives the request and the raw token string.
// Return true to allow the request, false to reject it.
ValidateFunc func(r *http.Request, token string) bool
}
BearerAuthConfig configures the Bearer Auth middleware behaviour.
Spec reference: https://www.rfc-editor.org/rfc/rfc6750
type CORSConfig ¶
type CORSConfig struct {
// AllowedOrigins is a list of exact origin strings, "*" for wildcard,
// or subdomain wildcard patterns like "https://*.example.com".
AllowedOrigins []string
// AllowOriginFunc is an optional dynamic callback invoked when the
// origin does not match any entry in AllowedOrigins. Return true to allow.
AllowOriginFunc func(origin string) bool
// AllowedMethods overrides the set of methods advertised in preflight
// and actual responses. When empty the middleware auto-discovers methods
// from the router for the matched route.
AllowedMethods []string
// AllowedHeaders lists the headers the client may send in the actual
// request. When empty the middleware reflects the Access-Control-Request-Headers
// value from the preflight request. Use "*" to reflect all requested headers.
AllowedHeaders []string
// ExposeHeaders lists the headers the browser may expose to client code.
ExposeHeaders []string
// AllowCredentials sets Access-Control-Allow-Credentials: true.
// Per the Fetch Standard, "*" cannot be used as Allow-Origin when
// credentials are enabled; the middleware returns ErrWildcardCredentials.
AllowCredentials bool
// MaxAge is the duration in seconds a preflight result may be cached.
// Positive values are sent as-is, negative values emit "0", zero omits the header.
MaxAge int
// OptionsStatusCode overrides the HTTP status code for preflight responses.
// When zero (default) the middleware uses 204 No Content.
OptionsStatusCode int
// OptionsPassthrough, when true, sets CORS headers on preflight but
// forwards the request to the next handler instead of terminating the chain.
OptionsPassthrough bool
// AllowPrivateNetwork, when true, responds to Access-Control-Request-Private-Network
// preflight headers with Access-Control-Allow-Private-Network: true.
// See https://wicg.github.io/private-network-access/
AllowPrivateNetwork bool
}
CORSConfig configures the CORS middleware behaviour.
Spec references:
- CORS protocol: https://fetch.spec.whatwg.org/#http-cors-protocol
- Web Origin: https://www.rfc-editor.org/rfc/rfc6454
- HTTP Vary: https://www.rfc-editor.org/rfc/rfc9110#field.vary
type CacheControlConfig ¶ added in v0.2.0
type CacheControlConfig struct {
// Rules is the ordered list of content type rules. The first matching
// rule wins. Required; at least one must be provided.
Rules []CacheControlRule
// DefaultValue is the Cache-Control header value for responses that
// don't match any rule. When empty, no header is set for unmatched
// types.
DefaultValue string
// DefaultExpires is the duration added to the current time to compute
// the Expires header for responses that don't match any rule. A zero
// duration produces a date in the past (epoch). A negative duration
// means no Expires header is set for unmatched types.
DefaultExpires time.Duration
}
CacheControlConfig configures the CacheControl middleware behaviour.
type CacheControlRule ¶ added in v0.2.0
type CacheControlRule struct {
// ContentType is a content type prefix to match against the response
// Content-Type (e.g. "image/", "application/json"). Matching is
// case-insensitive via strings.HasPrefix on the lowercased value.
ContentType string
// Value is the Cache-Control header value to set when this rule
// matches (e.g. "public, max-age=86400").
Value string
// Expires is the duration added to the current time to compute the
// Expires header value (formatted as HTTP-date per RFC 7231). A zero
// duration produces a date in the past (epoch), equivalent to
// "already expired". A negative duration means no Expires header is
// set for this rule. Positive values produce a future date
// (e.g. 24*time.Hour sets Expires to 24 hours from now).
Expires time.Duration
}
CacheControlRule maps a Content-Type prefix to Cache-Control and Expires header values.
type CanonicalHostConfig ¶ added in v0.7.0
type CanonicalHostConfig struct {
// URL is the canonical base URL to redirect to, including scheme
// and host (e.g. "https://www.example.com"). Required.
URL string
// StatusCode is the HTTP redirect status code. Defaults to
// 301 Moved Permanently.
StatusCode int
}
CanonicalHostConfig configures the Canonical Host middleware.
type CompressionConfig ¶ added in v0.2.0
type CompressionConfig struct {
// Level is the compression level for both gzip and deflate. When zero,
// flate.DefaultCompression is used. Must be in
// [flate.HuffmanOnly, flate.BestCompression] or zero.
Level int
// MinLength is the minimum response body size in bytes before compression
// is applied. When zero, all responses are compressed.
MinLength int
}
CompressionConfig configures the Compression middleware behaviour.
type ContentNegotiationConfig ¶ added in v0.7.0
type ContentNegotiationConfig struct {
// Offered is the list of media types the server can produce, in
// preference order. When empty, any media type from the Accept header
// is accepted and the best match is stored in context.
// Examples: "application/json", "application/xml", "text/html".
Offered []string
}
ContentNegotiationConfig configures the Content Negotiation middleware.
Spec reference: https://www.rfc-editor.org/rfc/rfc9110#section-12.5.1
type ContentTypeCheckConfig ¶ added in v0.2.0
type ContentTypeCheckConfig struct {
// AllowedTypes is the set of acceptable Content-Type values.
// Matching is case-insensitive and ignores parameters
// (e.g. "application/json" matches "application/json; charset=utf-8").
// Required; at least one must be provided.
AllowedTypes []string
// Methods is the set of HTTP methods that require Content-Type
// validation. When nil, defaults to POST, PUT, PATCH.
Methods []string
}
ContentTypeCheckConfig configures the Content-Type Check middleware behaviour.
type Drainer ¶ added in v0.15.0
type Drainer struct {
// contains filtered or unexported fields
}
Drainer is the control surface returned by GracefulShutdownMiddleware. Callers invoke Drain() from a signal handler to start rejecting new requests, then use Wait() to block until in-flight requests have completed (typically just before http.Server.Shutdown).
func GracefulShutdownMiddleware ¶ added in v0.15.0
func GracefulShutdownMiddleware(router *mux.Router, cfg GracefulShutdownConfig) (mux.MiddlewareFunc, *Drainer)
GracefulShutdownMiddleware returns a middleware that intercepts requests once Drain has been called and a Drainer the caller uses to trigger and observe the drain. Requests arriving before Drain() flow through unchanged; requests arriving after receive the configured drain response unless Bypass forwards them. In-flight requests are tracked via Drainer.InFlight and waited on via Drainer.Wait.
Pair this with http.Server.Shutdown: call Drainer.Drain on SIGTERM, Drainer.Wait to let active requests complete, then Server.Shutdown to close listeners.
The router is accepted so the Bypass predicate can resolve matched-route metadata. Pass the same *mux.Router the middleware is attached to via Use.
func (*Drainer) Drain ¶ added in v0.15.0
func (d *Drainer) Drain()
Drain marks the server as draining. After this call returns, every request that enters the middleware is rejected with the configured drain response unless Bypass forwards it. Idempotent.
func (*Drainer) InFlight ¶ added in v0.15.0
InFlight returns the number of requests currently inside the middleware chain. Useful for metrics and tests; counts only requests the middleware decided to forward to next (i.e. not drained, not bypassed-without-incrementing).
func (*Drainer) IsDraining ¶ added in v0.15.0
IsDraining reports whether Drain has been called.
func (*Drainer) Wait ¶ added in v0.15.0
Wait blocks until the in-flight count reaches zero or the context is cancelled. Returns nil on a clean drain or ctx.Err() if the deadline fires first. Wait is safe to call before, during, or after Drain; when no requests have ever been observed it returns immediately. The implementation polls inFlight at a 20ms cadence, which is invisible against typical shutdown deadlines and avoids per-request signalling overhead on the hot path.
The typical usage is:
drainer.Drain() shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() _ = drainer.Wait(shutdownCtx) _ = srv.Shutdown(shutdownCtx)
type EarlyHintsConfig ¶ added in v0.7.0
type EarlyHintsConfig struct {
// Links is the list of Link header values to send with the 103 Early
// Hints response. Each entry should follow the format defined in
// RFC 8288, e.g. `</style.css>; rel=preload; as=style`.
Links []string
// LinksFunc is called per request to compute Link header values
// dynamically. Its results are sent alongside the static Links list.
// Either Links or LinksFunc (or both) must be set.
LinksFunc func(r *http.Request) []string
}
EarlyHintsConfig configures the Early Hints middleware.
Spec reference: https://www.rfc-editor.org/rfc/rfc8297
type GracefulShutdownConfig ¶ added in v0.15.0
type GracefulShutdownConfig struct {
// Bypass, when non-nil, is consulted for every request; returning
// true forwards the request to the next handler even while the
// drain is in progress. The router is supplied so the predicate
// can inspect matched-route metadata. Typical uses: keep k8s
// liveness/readiness probes and /metrics reachable so the
// orchestrator can observe the drain.
Bypass func(*mux.Router, *http.Request) bool
// Response, when non-nil, fully owns the response written to
// requests that arrive after Drain() has been called. The
// middleware sets the default headers (Connection: close,
// Cache-Control: no-store, optional Retry-After) before invoking
// the handler so the handler can override them or write its own.
// StatusCode is ignored when Response is set because the handler
// controls its own status.
Response http.Handler
// StatusCode is the HTTP status code for the default drain
// response when Response is nil. Defaults to 503 Service
// Unavailable (RFC 9110 Section 15.6.4), which is the spec-correct
// signal that the server is intentionally rejecting new work.
StatusCode int
// RetryAfter, when greater than zero, emits a Retry-After header
// in delta-seconds form per RFC 9110 Section 10.2.3 on drain
// responses. Sub-second values round up to 1. Defaults to no
// header.
RetryAfter time.Duration
}
GracefulShutdownConfig configures the GracefulShutdown middleware.
type HTCPCPConfig ¶ added in v0.15.0
type HTCPCPConfig struct {
// PotType selects coffee pot or teapot semantics.
PotType PotType
// Teas lists the tea varieties this teapot can brew (RFC 7168
// Section 2.1.1). Names are matched case-insensitively against the
// Accept-Additions header. Ignored when PotType is PotCoffee. When
// nil for a teapot, DefaultTeaVarieties is used.
Teas []string
// AvailableAdditions lists the additions (milk-type, syrup-type,
// sweetener-type, etc., per RFC 2324 Section 2.2.2.1) the pot
// currently has on hand. Requests asking for an addition outside
// this set receive 406 Not Acceptable.
AvailableAdditions []string
// Empty signals that the pot has nothing to brew. BREW requests
// receive 503 Service Unavailable with a Retry-After header per
// RFC 2324 Section 2.3.3.
Empty bool
// RetryAfter is the value of the Retry-After header sent with 503
// responses, in seconds. Defaults to 60.
RetryAfter int
// ActiveOn restricts the days on which HTCPCP semantics apply. When
// the predicate returns false the middleware becomes a no-op and
// forwards the request to the next handler (which typically yields
// a 404 or 405 since BREW/WHEN are not real HTTP methods). When nil,
// IsAprilFirst is used, matching the publication date of RFC 2324.
// The argument is the time returned by Now.
ActiveOn func(time.Time) bool
// Now overrides the clock source used by ActiveOn. Defaults to
// time.Now. Intended for tests; production code should leave it nil.
Now func() time.Time
}
HTCPCPConfig configures the HTCPCP-TEA middleware.
type IPAllowConfig ¶ added in v0.6.0
type IPAllowConfig struct {
// Allowed is a list of IP addresses and CIDR ranges that are permitted
// to access the protected routes. Required; must contain at least one
// entry. Bare IPs are normalized to /32 (IPv4) or /128 (IPv6).
// Examples: "10.0.0.1", "192.168.0.0/16", "::1", "fd00::/8"
Allowed []string
// DeniedHandler is called when the client IP is not in the allowed
// list. When nil, a default handler returns 403 Forbidden with an
// empty body.
DeniedHandler http.Handler
}
IPAllowConfig configures the IP allow middleware.
type IdempotencyConfig ¶ added in v0.7.0
type IdempotencyConfig struct {
// Store is the backing store for cached responses. Required.
Store IdempotencyStore
// HeaderName overrides the header used to carry the idempotency key.
// Defaults to "Idempotency-Key".
HeaderName string
// TTL is the time-to-live for cached responses. Defaults to 24 hours.
// A zero value means entries do not expire.
TTL time.Duration
// Methods is the set of HTTP methods that require an idempotency key.
// When nil, defaults to POST.
Methods []string
// EnforceKey, when true, returns 400 Bad Request if the idempotency
// key header is missing on a matched method. When false (default),
// requests without the header are passed through without caching.
EnforceKey bool
// CacheableStatusCodes is an optional allow list of HTTP status codes
// that should be cached. When nil, all status codes are cached.
// When set, only responses with a status code in this list are stored;
// other responses (e.g. 500) are passed through without caching.
CacheableStatusCodes []int
// CacheKeyFunc is an optional function that builds the final cache key
// from the request and the raw idempotency key header value. Use this
// to scope cached responses per authenticated user, tenant, or other
// request-scoped identity. When nil, the default scoping
// (method + path + header value) is used.
CacheKeyFunc func(r *http.Request, key string) string
// ValidateKeyFunc is an optional function to validate the idempotency key
// format. When set, the middleware calls it before looking up the cache.
// It receives the request and the raw key value. Return true to accept
// the key, false to reject it with 400 Bad Request.
ValidateKeyFunc func(r *http.Request, key string) bool
// KeyMaxLength is the maximum allowed length of the idempotency key.
// Keys exceeding this length are rejected with 400 Bad Request.
// Defaults to 64. Set to -1 for no limit.
KeyMaxLength int
// CanCache is an optional pre-check called before any cache lookup or
// storage. When it returns false, the request is passed through to the
// handler without idempotency caching. Use this to skip caching based
// on request properties (e.g. specific paths, headers, or auth state).
// When nil, all matched requests are eligible for caching.
CanCache func(r *http.Request) bool
// OnCacheHit is called when a cached response is found for the
// idempotency key. Use this for observability (e.g. Prometheus counters).
// When nil, no callback is invoked.
OnCacheHit func(r *http.Request, key string)
// OnCacheMiss is called when no cached response is found and the
// handler is invoked. Use this for observability (e.g. Prometheus counters).
// When nil, no callback is invoked.
OnCacheMiss func(r *http.Request, key string)
// Locker is an optional distributed lock for in-flight requests.
// When set, the middleware acquires a lock before invoking the handler
// and releases it after the response is stored. If the lock cannot be
// acquired (another request with the same key is in progress), the
// middleware returns 409 Conflict. When nil, no locking is performed.
Locker IdempotencyLocker
// FingerprintFunc is an optional function that computes a fingerprint
// from the request. The fingerprint is stored alongside the cached
// response. On cache hit, if the current request's fingerprint does
// not match the stored one, the middleware returns 422 Unprocessable
// Entity instead of replaying the cached response. This prevents
// clients from reusing idempotency keys across different operations.
// When nil, no fingerprint matching is performed.
FingerprintFunc func(r *http.Request) string
// OnConflict is called when a 409 Conflict is returned because the
// Locker could not acquire a lock (another request with the same
// key is in-flight). Use this for observability.
// When nil, no callback is invoked.
OnConflict func(r *http.Request, key string)
// OnFingerprintMismatch is called when a 422 Unprocessable Entity is
// returned because the request fingerprint does not match the cached
// one. Use this for observability and alerting on key misuse.
// When nil, no callback is invoked.
OnFingerprintMismatch func(r *http.Request, key string)
// RetryAfter is the duration sent in the Retry-After header (as whole
// seconds) when a 409 Conflict response is returned due to an in-flight
// lock. When zero, no Retry-After header is sent.
RetryAfter time.Duration
// ReplayedHeaderName sets a response header to "true" when a cached
// response is replayed. Use this to let clients distinguish original
// responses from replays. When empty, no replay indicator header is
// set. Example: "X-Idempotency-Replayed".
ReplayedHeaderName string
// ErrorHandler is an optional function that writes error responses
// for all middleware-generated errors (400, 409, 422). When set, it
// replaces the default http.Error plain-text responses. Use this to
// return structured JSON errors or RFC 9457 Problem Details.
// When nil, http.Error is used.
ErrorHandler func(w http.ResponseWriter, r *http.Request, statusCode int)
// OnStore is called when a response is successfully stored in the
// cache. Use this for observability to track cache fill rate. Not
// called when the response status code is excluded by
// CacheableStatusCodes. When nil, no callback is invoked.
OnStore func(r *http.Request, key string, statusCode int)
// ResponseHeadersFunc is called before writing any response (original,
// replayed, or error). Use this to inject headers like X-Cache-Age or
// update the Date header. The replayed parameter is true when the
// response is a cached replay. When nil, no callback is invoked.
ResponseHeadersFunc func(w http.Header, r *http.Request, replayed bool)
// MaxCacheBodySize is the maximum response body size in bytes that
// will be cached. Responses with bodies exceeding this limit are
// served to the client but not stored in the cache. When zero, no
// limit is applied.
MaxCacheBodySize int64
}
IdempotencyConfig configures the Idempotency middleware.
Spec reference: https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/
type IdempotencyLocker ¶ added in v0.7.0
type IdempotencyLocker interface {
// Lock attempts to acquire a lock for the given key. Returns true if
// the lock was acquired, false if the key is already locked.
Lock(ctx context.Context, key string) bool
// Unlock releases the lock for the given key.
Unlock(ctx context.Context, key string)
}
IdempotencyLocker is an optional interface for distributed locking of in-flight requests. When a lock cannot be acquired (another request with the same key is in progress), the middleware returns 409 Conflict. Implementations must be safe for concurrent use.
type IdempotencyStore ¶ added in v0.7.0
type IdempotencyStore interface {
// Get retrieves a cached response by key. Returns the serialized
// response and true if found, or nil and false if not cached.
Get(ctx context.Context, key string) ([]byte, bool)
// Set stores a serialized response with the given key and TTL.
// A zero TTL means the entry does not expire.
Set(ctx context.Context, key string, value []byte, ttl time.Duration)
}
IdempotencyStore is the interface for storing and retrieving cached responses keyed by idempotency key. Implementations must be safe for concurrent use.
type MaintenanceConfig ¶ added in v0.15.0
type MaintenanceConfig struct {
// Enabled reports whether the maintenance response should be sent
// for the current request. The predicate is consulted on every
// request, so callers can flip maintenance on and off by mutating
// the data structure (atomic.Bool, file, env, scheduled window)
// the predicate reads. When nil, the middleware is a no-op and
// every request passes through.
Enabled func(*http.Request) bool
// Bypass, when non-nil, is consulted before Enabled is checked; if
// it returns true the request bypasses maintenance entirely. The
// router is supplied so the predicate can inspect matched-route
// metadata, allow lists, header tokens, etc. Typical uses: admin
// IP allow-list, internal health checks, deploy-pipeline tooling.
Bypass func(*mux.Router, *http.Request) bool
// Response, when non-nil, fully owns the maintenance response
// body. The middleware sets Retry-After (when configured) and then
// invokes the handler; StatusCode is ignored because the handler
// controls its own status. Use this to render an HTML page, return
// RFC 9457 ProblemDetails JSON, redirect to a static maintenance
// page, or anything else.
Response http.Handler
// StatusCode is the HTTP status code for the default response when
// Response is nil. Defaults to 503 Service Unavailable (RFC 9110
// Section 15.6.4), which is the spec-correct signal for scheduled
// maintenance; other codes are an escape hatch and should be used
// with a clear reason.
StatusCode int
// RetryAfter, when greater than zero and RetryAt is the zero value,
// emits a Retry-After header in delta-seconds form per RFC 9110
// Section 10.2.3. Sub-second values are rounded down.
RetryAfter time.Duration
// RetryAt, when non-zero, emits a Retry-After header in HTTP-date
// form per RFC 9110 Section 10.2.3, overriding RetryAfter. Use
// when maintenance has a scheduled end time. Times are formatted
// in UTC.
RetryAt time.Time
}
MaintenanceConfig configures the MaintenanceMode middleware.
type MethodOverrideConfig ¶ added in v0.2.0
type MethodOverrideConfig struct {
// HeaderNames is the list of header names checked in order.
// The first non-empty header value is used as the override.
// When nil, defaults to
// ["X-HTTP-Method-Override", "X-Method-Override", "X-HTTP-Method"].
HeaderNames []string
// OriginalMethods is the set of HTTP methods eligible for override.
// When nil, defaults to [POST].
OriginalMethods []string
// AllowedMethods restricts which methods can be used as overrides.
// When nil, defaults to PUT, PATCH, DELETE, HEAD, OPTIONS.
AllowedMethods []string
}
MethodOverrideConfig configures the Method Override middleware behaviour.
type NoCacheConfig ¶ added in v0.15.0
type NoCacheConfig struct {
// Preset selects which header set the middleware emits. Defaults to
// NoCachePresetModern (Cache-Control: no-store only).
Preset NoCachePreset
// Skip, when non-nil, is consulted before each response is flushed;
// returning true forwards the handler's response unchanged, leaving
// any handler-set caching headers intact. The router is supplied so
// callers can inspect matched-route metadata (e.g. opt specific
// routes out of the no-cache policy).
Skip func(*mux.Router, *http.Request) bool
}
NoCacheConfig configures the NoCache middleware.
type NoCachePreset ¶ added in v0.15.0
type NoCachePreset int
NoCachePreset selects the set of response headers NoCacheMiddleware writes on each response.
const ( // NoCachePresetModern emits a single Cache-Control: no-store header // per RFC 9111 Section 5.2.2.5, which instructs shared and private // caches not to store any part of the response. Sufficient for any // client and intermediary that respects RFC 9111. NoCachePresetModern NoCachePreset = iota // NoCachePresetStrict emits the legacy "no-cache combo" expected by // caches and clients predating RFC 7234 / RFC 9111: // // Cache-Control: no-store, no-cache, must-revalidate, max-age=0, // private // Pragma: no-cache // Expires: 0 // // Pragma comes from RFC 1945; Expires: 0 is the HTTP/1.0 convention // for "already expired". Use when downstream caches may be ancient // or non-conformant. NoCachePresetStrict )
type PatchRoutingConfig ¶ added in v0.7.0
type PatchRoutingConfig struct {
// AllowedTypes is the set of accepted Content-Type values for PATCH
// requests. Matching is case-insensitive and ignores parameters.
// When nil, defaults to application/json, application/merge-patch+json,
// and application/json-patch+json.
AllowedTypes []string
}
PatchRoutingConfig configures the Patch Routing middleware.
type PotType ¶ added in v0.15.0
type PotType int
PotType identifies the kind of pot the middleware represents.
const ( // PotCoffee is a coffee pot. Brews coffee; refuses tea per RFC 7168 // Section 2.1.1 (only teapots brew tea). PotCoffee PotType = iota // PotTeapot is a teapot. Brews tea; refuses coffee with 418 per // RFC 2324 Section 2.3.2 and RFC 7168 Section 2.3.3. PotTeapot )
type ProblemDetails ¶ added in v0.7.0
type ProblemDetails struct {
// Type is a URI reference that identifies the problem type. When
// dereferenced, it should provide human-readable documentation.
// Defaults to "about:blank" when empty, per RFC 9457 Section 3.1.3.
Type string `json:"type"`
// Title is a short, human-readable summary of the problem type.
// It should not change from occurrence to occurrence of the same
// problem type, per RFC 9457 Section 3.1.4.
Title string `json:"title"`
// Status is the HTTP status code for this occurrence of the problem.
// Per RFC 9457 Section 3.1.1.
Status int `json:"status"`
// Detail is a human-readable explanation specific to this occurrence
// of the problem, per RFC 9457 Section 3.1.5.
Detail string `json:"detail,omitempty"`
// Instance is a URI reference that identifies the specific occurrence
// of the problem, per RFC 9457 Section 3.1.2.
Instance string `json:"instance,omitempty"`
// Extensions contains additional members beyond the standard fields.
// Per RFC 9457 Section 3.2, problem types may extend the object with
// additional members that provide further context.
Extensions map[string]any `json:"-"`
}
ProblemDetails represents an RFC 9457 Problem Details object.
Spec reference: https://www.rfc-editor.org/rfc/rfc9457
func NewProblemDetails ¶ added in v0.7.0
func NewProblemDetails(status int) ProblemDetails
NewProblemDetails creates a ProblemDetails with the given status code and the standard status text as title. Type defaults to "about:blank" per RFC 9457 Section 4.2.
func (ProblemDetails) MarshalJSON ¶ added in v0.7.0
func (p ProblemDetails) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler. It serializes the standard fields and merges any extension members into the top-level JSON object, per RFC 9457 Section 3.2.
type ProxyHeadersConfig ¶
type ProxyHeadersConfig struct {
// TrustedProxies is a list of IP addresses and CIDR ranges.
// Forwarding headers are only honoured when r.RemoteAddr is in this set.
// When empty, DefaultTrustedProxies (private/loopback ranges) is used.
// Examples: "10.0.0.1", "192.168.0.0/16", "::1", "fd00::/8"
TrustedProxies []string
// EnableForwarded enables parsing of the RFC 7239 Forwarded header.
// When enabled, the Forwarded header is used as a fallback after the
// de-facto X-Forwarded-* and X-Real-IP headers.
//
// Spec reference: https://www.rfc-editor.org/rfc/rfc7239
EnableForwarded bool
}
ProxyHeadersConfig configures the ProxyHeaders middleware behaviour.
type RecoveryConfig ¶ added in v0.2.0
type RecoveryConfig struct {
// LogFunc is an optional callback invoked with the request and the
// recovered value when a panic occurs. When nil, no logging is performed.
LogFunc func(r *http.Request, err any)
}
RecoveryConfig configures the Recovery middleware behaviour.
type RedirectConfig ¶ added in v0.8.0
type RedirectConfig struct {
// Rules is the list of redirect rules evaluated in order.
// The first matching rule wins.
Rules []RedirectRule
// StatusCode is the default HTTP redirect status code.
// Defaults to 307 Temporary Redirect.
StatusCode int
}
RedirectConfig configures the Redirect middleware.
type RedirectRule ¶ added in v0.8.0
type RedirectRule struct {
// From is the path to match. Must start with "/".
// A trailing "*" enables prefix matching: "/old/*" matches any path
// starting with "/old/" and appends the remainder to To.
// Without "*", only exact path matches trigger a redirect.
From string
// To is the redirect target. For prefix rules, the matched suffix
// is appended. Can be an absolute URL for external redirects.
To string
// StatusCode is the HTTP redirect status code for this rule.
// Overrides the default from RedirectConfig. If zero, the config
// default is used.
StatusCode int
}
RedirectRule defines a single redirect mapping.
type RequestIDConfig ¶ added in v0.2.0
type RequestIDConfig struct {
// HeaderName overrides the header used to propagate the request ID.
// Defaults to "X-Request-ID" when empty.
HeaderName string
// GenerateFunc is an optional callback that returns a new unique ID.
// It receives the current request, allowing ID generation based on
// request context. Defaults to GenerateUUIDv4.
GenerateFunc func(r *http.Request) string
// TrustIncoming, when true, reuses an existing request ID from the
// incoming request header instead of generating a new one.
TrustIncoming bool
}
RequestIDConfig configures the Request ID middleware behaviour.
type RequestSizeLimitConfig ¶ added in v0.2.0
type RequestSizeLimitConfig struct {
// MaxBytes is the maximum allowed request body size in bytes.
// Must be greater than zero.
MaxBytes int64
}
RequestSizeLimitConfig configures the Request Size Limit middleware behaviour.
type SecurityHeadersConfig ¶ added in v0.2.0
type SecurityHeadersConfig struct {
// DisableContentTypeNosniff disables the X-Content-Type-Options: nosniff
// header. The header is set by default (when false).
DisableContentTypeNosniff bool
// FrameOption sets the X-Frame-Options header value.
// Valid values are "DENY", "SAMEORIGIN", or empty string to skip.
// Defaults to "DENY".
FrameOption string
// ReferrerPolicy sets the Referrer-Policy header value.
// Defaults to "strict-origin-when-cross-origin".
ReferrerPolicy string
// HSTSMaxAge sets the max-age directive for the Strict-Transport-Security
// header in seconds. When zero, the header is not set.
HSTSMaxAge int
// HSTSIncludeSubDomains appends the includeSubDomains directive to the
// Strict-Transport-Security header. Only effective when HSTSMaxAge > 0.
HSTSIncludeSubDomains bool
// HSTSPreload appends the preload directive to the
// Strict-Transport-Security header. Only effective when HSTSMaxAge > 0.
HSTSPreload bool
// CrossOriginOpenerPolicy sets the Cross-Origin-Opener-Policy header.
// When empty, the header is not set.
CrossOriginOpenerPolicy string
// ContentSecurityPolicy sets the Content-Security-Policy header.
// When empty, the header is not set.
ContentSecurityPolicy string
// PermissionsPolicy sets the Permissions-Policy header.
// When empty, the header is not set.
PermissionsPolicy string
}
SecurityHeadersConfig configures the Security Headers middleware behaviour.
type ServerConfig ¶ added in v0.2.0
type ServerConfig struct {
// Hostname is the value written to the X-Server-Hostname response
// header. Resolution order: Hostname field, then HostnameEnv
// environment variable, then os.Hostname.
Hostname string
// HostnameEnv is a list of environment variable names checked in
// order (e.g. ["POD_NAME", "HOSTNAME"]). The first non-empty
// value is used. Only consulted when Hostname is empty. When all
// variables are unset or empty, os.Hostname is used as a fallback.
HostnameEnv []string
}
ServerConfig configures the Server middleware behaviour.
type StaticFilesConfig ¶ added in v0.2.0
type StaticFilesConfig struct {
// FS is the file system to serve files from. Required.
// Works with os.DirFS, embed.FS, and any fs.FS implementation.
FS fs.FS
// EnableDirectoryListing allows directory contents to be listed
// when no index.html is present. Disabled by default for security.
EnableDirectoryListing bool
// SPAFallback serves the root index.html for any path that does
// not match an existing file. This allows client-side routers to
// handle all routes. Requires index.html at the root of FS.
SPAFallback bool
// EnableETag precomputes strong ETags for all files by walking the
// FS at init time. Designed for immutable file systems such as
// embed.FS. The handler sets the ETag response header and handles
// If-None-Match conditional requests (304 Not Modified).
EnableETag bool
// PathPrefix is the URL path prefix under which the handler is
// mounted. The handler strips this prefix before looking up files
// in the FS, replacing the need for http.StripPrefix.
PathPrefix string
// Aliases maps URL paths to file paths in the FS. Keys are
// relative to PathPrefix (the prefix is stripped before matching).
// Alias targets are validated at init time. ETag support applies
// to aliased paths.
//
// Example:
//
// Aliases: map[string]string{
// "/policy-builder/": "policy-builder.html",
// "/policy-playground/": "policy-playground.html",
// }
Aliases map[string]string
}
StaticFilesConfig configures the static file handler.
type SunsetConfig ¶ added in v0.6.0
type SunsetConfig struct {
// Sunset is the point in time when the resource is expected to become
// unresponsive. Serialized as an HTTP-date per RFC 7231 Section 7.1.1.1.
// Required.
//
// See: https://www.rfc-editor.org/rfc/rfc8594#section-3
Sunset time.Time
// Deprecation is the point in time when the resource was deprecated.
// When non-zero, the Deprecation response header is set.
Deprecation time.Time
// Link is an optional URI pointing to documentation about the
// deprecation or sunset. When non-empty, a Link header with
// rel="sunset" is added to the response.
//
// See: https://www.rfc-editor.org/rfc/rfc8594#section-4
Link string
}
SunsetConfig configures the Sunset middleware.
type TimeoutConfig ¶ added in v0.2.0
type TimeoutConfig struct {
// Duration is the maximum time allowed for the handler to complete.
// Must be greater than zero.
Duration time.Duration
// Message is the response body returned when the handler times out.
// When empty, the standard library default is used.
Message string
}
TimeoutConfig configures the Timeout middleware behaviour.
Source Files
¶
- acceptpatch.go
- accesslog.go
- basicauth.go
- bearerauth.go
- cachecontrol.go
- canonicalhost.go
- compression.go
- contentnegotiation.go
- contenttypecheck.go
- cors.go
- doc.go
- earlyhints.go
- gracefulshutdown.go
- htcpcp.go
- idempotency.go
- ipallow.go
- maintenance.go
- methodoverride.go
- nocache.go
- patchtype.go
- problemdetails.go
- profiler.go
- proxyheaders.go
- recovery.go
- redirect.go
- requestid.go
- requestsizelimit.go
- securityheaders.go
- server.go
- staticfiles.go
- sunset.go
- timeout.go