Documentation
¶
Overview ¶
Package middlewares is a set of production-ready HTTP middleware for the standard net/http. Every middleware has the ordinary shape func(http.Handler) http.Handler, so it works with any router: the standard http.ServeMux, the goloop/mux router, or hand-written handlers.
It is not a framework. It closes the common cross-cutting needs that the standard library leaves out - request identifiers, real client IP, panic recovery, request logging, timeouts, response compression, rate limiting, concurrency throttling, CORS and security headers - with no third-party dependencies.
Compose middleware with Chain, or apply them one-shot with Handler:
h := middlewares.Chain(
middlewares.RequestID(),
middlewares.RealIP(),
middlewares.Recoverer(),
middlewares.Logger(),
middlewares.Compress(),
)(mux)
http.ListenAndServe(":8080", h)
Logging uses the standard log/slog. Pass any *slog.Logger through the relevant option; the package never imports a concrete logging backend.
Security notes worth reading before production use:
Rate limiting and throttling are different tools ¶
RateLimit answers "how often may this client come back"; Throttle answers "how much of this work fits in the process at once". Ten per minute still admits ten in the same instant, which is what exhausts memory in front of an expensive handler; ten concurrent slots do not, and neither stops a client from returning every second forever. In front of sign-in both belong.
middlewares.RateLimit(middlewares.RateLimitConfig{
Limit: 20, Window: 15 * time.Minute,
})
The store decides, and the default one counts only what this process saw. A limit that has to hold across instances needs a RateLimitStore they share; the interface is one method so that a Redis or database implementation is a short piece of application code rather than a dependency of this package.
When a store cannot answer, the request is refused with 503 rather than served. A limiter usually sits in front of sign-in, password reset and one-time codes, and a store outage that quietly removed the limit from exactly those routes would turn an availability problem into a security one. WithFailOpen chooses the other trade for routes where it is the right one.
Anything keyed by client address inherits the caveat on RealIP: behind a reverse proxy with no trusted-proxy policy, every client resolves to the proxy and the limit becomes one budget shared by everyone. Configure WithTrustedProxies before relying on KeyByIP.
Correlated logs ¶
Logger writes one line per request. WithContextLogger additionally puts a logger into the request context, already carrying the request identifier, method and path, so that handler logs join up with it:
h := middlewares.Logger(middlewares.WithContextLogger())
// in a handler:
middlewares.LoggerFrom(r.Context()).Warn("upstream refused", "status", code)
The alternative is remembering to add the identifier at every call site, which fails quietly and selectively - the lines that are missing it are the ones written in a hurry, which are the ones written during an incident.
- RealIP trusts no proxy headers by default; configure trusted proxies explicitly, or a client can spoof its address.
- Timeout cancels the request context but cannot stop a handler goroutine that ignores it.
- Recoverer cannot change a response that was already committed; it only logs the panic in that case.
- Compress never touches text/event-stream, already-encoded responses, or bodies below the minimum size.
- CORS allows nothing until you name the origins; wildcard origin together with credentials is rejected at construction.
Index ¶
- Constants
- func Handler(next http.Handler, m ...Middleware) http.Handler
- func KeyByHeader(name string) func(*http.Request) string
- func KeyByIP() func(*http.Request) string
- func LoggerFrom(ctx context.Context) *slog.Logger
- func RealIPFrom(ctx context.Context) string
- func RequestIDFrom(ctx context.Context) string
- type CORSOption
- type CompressOption
- type HSTS
- type LoggerOption
- type MemoryRateLimitStore
- type Middleware
- func CORS(opts ...CORSOption) Middleware
- func Chain(m ...Middleware) Middleware
- func Compress(opts ...CompressOption) Middleware
- func Logger(opts ...LoggerOption) Middleware
- func MaxBytes(n int64) Middleware
- func RateLimit(cfg RateLimitConfig, opts ...RateLimitOption) Middleware
- func RealIP(opts ...RealIPOption) Middleware
- func Recoverer(opts ...RecovererOption) Middleware
- func RequestID(opts ...RequestIDOption) Middleware
- func SecurityHeaders(opts ...SecurityOption) Middleware
- func Throttle(limit int, opts ...ThrottleOption) Middleware
- func Timeout(d time.Duration, opts ...TimeoutOption) Middleware
- type RateLimitConfig
- type RateLimitOption
- type RateLimitResult
- type RateLimitStore
- type RealIPOption
- type RecovererOption
- type RequestIDOption
- type SecurityOption
- type ThrottleOption
- type TimeoutOption
Examples ¶
Constants ¶
const DefaultCompressMinBytes = 1024
DefaultCompressMinBytes is the smallest response, in bytes, that Compress will gzip. Smaller bodies are sent as-is, since compression would not help.
const DefaultRequestIDHeader = "X-Request-ID"
DefaultRequestIDHeader is the header read and written by RequestID unless overridden with WithRequestIDHeader.
Variables ¶
This section is empty.
Functions ¶
func Handler ¶
func Handler(next http.Handler, m ...Middleware) http.Handler
Handler applies middleware to next in one call. It is shorthand for Chain(m...)(next).
func KeyByHeader ¶ added in v0.3.0
KeyByHeader groups requests by a header value, for limits keyed by an API key or a tenant. Requests without the header fall back to the client address, so a missing header cannot buy an unlimited budget.
func KeyByIP ¶ added in v0.3.0
KeyByIP groups requests by client address, using the address RealIP resolved when it ran.
Read the note on RealIP before relying on this. Without WithTrustedProxies, every client behind a reverse proxy resolves to the proxy's own address, so this returns one key for all of them and the limit becomes a budget the whole userbase shares. Nothing errors; the limiter simply stops being per-client.
func LoggerFrom ¶ added in v0.3.0
LoggerFrom returns the logger WithContextLogger put in the context, carrying the request identifier, method and path. Without that option, or outside a request, it returns slog.Default so a caller never has to check:
middlewares.LoggerFrom(r.Context()).Info("upstream refused", "status", code)
The line then carries the identifier of the request that caused it, which is what makes the two halves of an incident join up.
func RealIPFrom ¶
RealIPFrom returns the client IP resolved by the RealIP middleware, or an empty string if none is present.
This is the only way to read that address: RealIP writes it here and leaves Request.RemoteAddr untouched. An empty result means the middleware did not run, and falling back to RemoteAddr is then the caller's decision to make knowingly - behind a proxy it is the proxy's address.
func RequestIDFrom ¶
RequestIDFrom returns the request identifier stored by the RequestID middleware, or an empty string if none is present.
Types ¶
type CORSOption ¶
type CORSOption func(*corsConfig)
CORSOption configures the CORS middleware.
func WithAllowCredentials ¶
func WithAllowCredentials() CORSOption
WithAllowCredentials allows credentialed requests (cookies, HTTP auth). It cannot be combined with a wildcard origin.
func WithAllowedHeaders ¶
func WithAllowedHeaders(headers ...string) CORSOption
WithAllowedHeaders sets the request headers advertised in preflight responses. When unset, the requested headers are reflected back.
func WithAllowedMethods ¶
func WithAllowedMethods(methods ...string) CORSOption
WithAllowedMethods sets the methods advertised in preflight responses.
func WithAllowedOrigins ¶
func WithAllowedOrigins(origins ...string) CORSOption
WithAllowedOrigins sets the origins permitted to make cross-origin requests. The special value "*" allows any origin. Nothing is allowed until this is set.
func WithExposedHeaders ¶
func WithExposedHeaders(headers ...string) CORSOption
WithExposedHeaders sets the response headers browsers may expose to script.
func WithMaxAge ¶
func WithMaxAge(seconds int) CORSOption
WithMaxAge sets how long, in seconds, a preflight result may be cached.
type CompressOption ¶
type CompressOption func(*compressConfig)
CompressOption configures the Compress middleware.
func WithCompressLevel ¶
func WithCompressLevel(level int) CompressOption
WithCompressLevel sets the gzip level (gzip.BestSpeed to gzip.BestCompression, plus gzip.DefaultCompression and gzip.HuffmanOnly). An out-of-range level is ignored and the default is kept, so a configuration mistake cannot turn into a deferred runtime nil-pointer panic on the first large request.
func WithCompressMinBytes ¶
func WithCompressMinBytes(n int) CompressOption
WithCompressMinBytes sets the minimum response size to compress.
func WithCompressTypes ¶
func WithCompressTypes(types ...string) CompressOption
WithCompressTypes replaces the set of compressible content types (matched on the media type, without parameters).
type HSTS ¶ added in v0.3.0
type HSTS struct {
// MaxAge is how long a browser should refuse plain HTTP for this host.
// It is a duration rather than a count of seconds because a year is
// easier to read as 365*24*time.Hour than as 31536000. Values below a
// second round down to zero, which omits the header.
MaxAge time.Duration
// IncludeSubDomains extends the policy to every subdomain. Almost every
// real policy wants it, and it is worth meaning: a subdomain that cannot
// serve HTTPS becomes unreachable for as long as MaxAge lasts.
IncludeSubDomains bool
// Preload asks for inclusion in the browsers' built-in list. It requires
// IncludeSubDomains and a long MaxAge, and removal from that list takes
// months, so set it only for a host that will be HTTPS-only indefinitely.
Preload bool
}
HSTS is a complete Strict-Transport-Security policy.
type LoggerOption ¶
type LoggerOption func(*loggerConfig)
LoggerOption configures the Logger middleware.
func WithContextLogger ¶ added in v0.3.0
func WithContextLogger() LoggerOption
WithContextLogger puts a logger for this request into its context, already carrying the request identifier, method and path, so that handlers can log with LoggerFrom and have their lines correlate without threading anything through.
It is worth the option because the alternative fails quietly. Logger writes one line per request with the identifier on it, and every line a handler writes is separate and unrelated; joining them afterwards means matching on timestamps. The usual fix is to remember to add the identifier at each call site, and what actually happens is that someone does not - including people who have just written in a design document that the logs correlate.
It is opt-in rather than automatic because it allocates a context and a logger per request, and a service that never reads it should not pay for it.
func WithLogLevel ¶
func WithLogLevel(level slog.Level) LoggerOption
WithLogLevel sets the level at which requests are logged (default Info).
func WithLogMessage ¶
func WithLogMessage(msg string) LoggerOption
WithLogMessage sets the log message (default "http request").
func WithLogger ¶
func WithLogger(l *slog.Logger) LoggerOption
WithLogger sets the slog.Logger used for request logs. When nil (the default), slog.Default is used.
type MemoryRateLimitStore ¶ added in v0.3.0
type MemoryRateLimitStore struct {
// contains filtered or unexported fields
}
MemoryRateLimitStore is a token bucket per key, held in this process.
A bucket refills continuously rather than resetting on a schedule, which is what keeps a burst at a window boundary from being twice the limit: a fixed window that resets at noon will happily serve a full budget at 11:59:59 and another at 12:00:00. The bucket also lets a caller who has been quiet spend a little faster, up to the limit, which is usually what "ten per minute" was meant to allow.
It counts only what this process saw. Behind a load balancer with several instances, each keeps its own buckets and the effective limit is multiplied by the number of instances; a limit that has to hold across them needs a store they share.
func NewMemoryRateLimitStore ¶ added in v0.3.0
func NewMemoryRateLimitStore() *MemoryRateLimitStore
NewMemoryRateLimitStore returns an empty in-process token-bucket store.
type Middleware ¶
Middleware is the standard net/http middleware shape: it wraps a handler and returns a new handler.
It is an alias rather than a defined type on purpose. Routers declare their own name for this same shape, and a defined type here would force an explicit conversion at every boundary; an alias makes everything this package returns assignable to any of them directly.
func CORS ¶
func CORS(opts ...CORSOption) Middleware
CORS handles Cross-Origin Resource Sharing. Its defaults are safe: no origin is allowed until you name one with WithAllowedOrigins. A preflight OPTIONS request from an allowed origin is answered with 204 and the appropriate headers; other requests are annotated and passed through.
CORS panics if credentials are allowed together with a wildcard origin, which is an invalid and unsafe combination.
Example ¶
ExampleCORS answers a preflight request from an allowed origin.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"github.com/goloop/middlewares"
)
func main() {
h := middlewares.CORS(
middlewares.WithAllowedOrigins("https://app.example.com"),
middlewares.WithAllowedMethods("GET", "POST"),
)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
r := httptest.NewRequest(http.MethodOptions, "/", nil)
r.Header.Set("Origin", "https://app.example.com")
r.Header.Set("Access-Control-Request-Method", "POST")
rec := httptest.NewRecorder()
h.ServeHTTP(rec, r)
fmt.Println(rec.Code)
fmt.Println(rec.Header().Get("Access-Control-Allow-Origin"))
}
Output: 204 https://app.example.com
func Chain ¶
func Chain(m ...Middleware) Middleware
Chain composes middleware into a single Middleware. The first middleware in the list is the outermost wrapper, so it runs first on the way in and last on the way out:
mw := middlewares.Chain(a, b, c) // request flows a -> b -> c -> handler
Example ¶
ExampleChain composes several middleware into one wrapper.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"github.com/goloop/middlewares"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /hello", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "hello")
})
h := middlewares.Chain(
middlewares.RequestID(),
middlewares.Recoverer(),
middlewares.SecurityHeaders(),
)(mux)
rec := httptest.NewRecorder()
h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/hello", nil))
fmt.Println(rec.Body.String())
fmt.Println(rec.Header().Get("X-Content-Type-Options"))
}
Output: hello nosniff
func Compress ¶
func Compress(opts ...CompressOption) Middleware
Compress gzips responses for clients that accept it. It buffers the response until it reaches the minimum size, then decides whether to compress based on the status, existing Content-Encoding and Content-Type. It never compresses HEAD responses, 204/304 replies, already-encoded bodies, text/event-stream (server-sent events) or bodies below the minimum size. The Vary: Accept-Encoding header is always set on a compressed response.
func Logger ¶
func Logger(opts ...LoggerOption) Middleware
Logger logs one line per request with method, path, status, response size and duration, using log/slog. It does no work when the request is skipped or when the logger is not enabled at the configured level. The request identifier from RequestID, if present, is included.
func MaxBytes ¶
func MaxBytes(n int64) Middleware
MaxBytes limits the size of a request body to n bytes using http.MaxBytesReader. A handler that reads past the limit gets an error, and the server responds appropriately. Use it to guard against oversized uploads.
func RateLimit ¶ added in v0.3.0
func RateLimit(cfg RateLimitConfig, opts ...RateLimitOption) Middleware
RateLimit limits how often one caller may make a request.
It is not Throttle and does not replace it. A rate limit answers "how often may this client come back"; Throttle answers "how much of this work fits in the process at once". Ten per minute still admits ten in the same instant, which is what exhausts memory in front of an expensive handler; ten concurrent slots do not, and neither stops a client from returning every second forever. In front of sign-in both belong.
A rejected request gets the configured status, a Retry-After header, and the X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers. So does an allowed one, minus Retry-After, so a well-behaved client can slow down before it is refused.
RateLimit panics if Limit is not positive or Window is not positive, since either is a programming error rather than a runtime condition.
func RealIP ¶
func RealIP(opts ...RealIPOption) Middleware
RealIP resolves the client IP and stores it in the request context, readable with RealIPFrom.
The context is the only place it writes. It does not rewrite Request.RemoteAddr, and nothing downstream that reads RemoteAddr sees a resolved address - a handler doing so gets the peer, which behind a proxy is the proxy. Anything that wants the client address has to ask RealIPFrom for it; that is the whole interface.
By default it uses the direct peer address (RemoteAddr) and ignores forwarded headers, which is the safe choice: without a trusted proxy policy a client can spoof X-Forwarded-For. Configure WithTrustedProxies (or TrustLoopbackProxies) to honor forwarded headers from known proxies.
Know what that default costs behind a reverse proxy. If requests arrive through nginx, a dev server or anything else in front, the direct peer is that proxy, so every client resolves to one address. Nothing errors and nothing looks wrong: rate limits, audit trails and ban lists keyed by IP simply become shared by everyone at once. A limit of twenty attempts per address is then twenty for the whole userbase, and the second person to mistype a password is locked out by the first.
That is the price of not trusting a header anyone can forge, and it is the right default - but it is only correct once WithTrustedProxies tells this middleware which peer is the proxy. Configure it before relying on anything keyed by client address.
func Recoverer ¶
func Recoverer(opts ...RecovererOption) Middleware
Recoverer catches a panic from a downstream handler, logs it with a stack trace and turns it into a response. If the handler had already committed a response (status and some body written), Recoverer cannot change it and only logs the panic. http.ErrAbortHandler is re-panicked so the server can abort the connection as usual.
func RequestID ¶
func RequestID(opts ...RequestIDOption) Middleware
RequestID assigns each request an identifier, stores it in the request context (read with RequestIDFrom) and echoes it in the response header. By default it generates a new 128-bit random identifier and ignores any inbound value; use WithTrustedRequestIDHeader to accept a safe client-supplied one.
Example ¶
ExampleRequestID reuses a fixed generator so the identifier is predictable.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"github.com/goloop/middlewares"
)
func main() {
h := middlewares.RequestID(
middlewares.WithRequestIDGenerator(func() string { return "req-1" }),
)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println(middlewares.RequestIDFrom(r.Context()))
}))
h.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "/", nil))
}
Output: req-1
func SecurityHeaders ¶
func SecurityHeaders(opts ...SecurityOption) Middleware
SecurityHeaders adds a conservative set of security response headers: X-Content-Type-Options, X-Frame-Options and Referrer-Policy by default, with optional HSTS and Content-Security-Policy. Existing header values set by the handler are not overwritten.
func Throttle ¶
func Throttle(limit int, opts ...ThrottleOption) Middleware
Throttle bounds how many requests run at once: a semaphore over the handler, shedding load instead of queueing it. It is an in-process concurrency limiter, not a rate limiter, and the two answer different questions: a rate limit says how often a client may come back, a concurrency limit says how much of this work the process can afford at the same moment. A rate limit of ten per minute still admits ten requests in the same second; ten concurrent slots do not, which is what matters in front of work that pays its cost per call - password hashing at tens of megabytes per verification is the classic case, where the limit turns a spike into a bounded memory ceiling.
When every slot is busy the request is rejected immediately with the configured status rather than queued. The waiters themselves would be cheap to hold; what a queue actually does is pin each caller's connection while its deadline runs out, so the process ends up doing expensive work for clients that have already given up. An immediate rejection with Retry-After tells them honestly to come back later.
Throttle panics if limit is not positive, since that is a programming error.
func Timeout ¶
func Timeout(d time.Duration, opts ...TimeoutOption) Middleware
Timeout enforces a per-request deadline. It cancels the request context after d and, if the handler has not finished, replies with 503 Service Unavailable. It is built on http.TimeoutHandler, so the timeout reply and the handler's own writes are synchronized and cannot race.
Timeout cannot stop a handler goroutine that ignores its context. Long handlers must observe r.Context().Done() themselves to actually stop working.
type RateLimitConfig ¶ added in v0.3.0
type RateLimitConfig struct {
// Key groups requests that share a budget. Nil means [KeyByIP].
Key func(*http.Request) string
// Limit is how many requests one key may make per Window. A limit of
// zero or less is a programming error and panics at construction.
Limit int
Window time.Duration
// Store decides. Nil means a fresh [NewMemoryRateLimitStore], which is
// per-process: with more than one instance running, each gets its own
// budget and the effective limit multiplies by the instance count.
Store RateLimitStore
}
RateLimitConfig describes a limit.
type RateLimitOption ¶ added in v0.3.0
type RateLimitOption func(*rateLimitConfig)
RateLimitOption configures the RateLimit middleware.
func WithFailOpen ¶ added in v0.3.0
func WithFailOpen() RateLimitOption
WithFailOpen serves requests when the store cannot answer, instead of refusing them.
The default is the other way round, and deliberately: a limiter is usually in front of sign-in, password reset or one-time codes, and a store outage that silently removed the limit from exactly those routes would be an availability problem turning itself into a security one. Choose this for routes where being reachable matters more than being limited - and know that while the store is down, they are not limited at all.
func WithRateLimitStatus ¶ added in v0.3.0
func WithRateLimitStatus(code int) RateLimitOption
WithRateLimitStatus sets the status returned when the limit is reached (default 429 Too Many Requests).
type RateLimitResult ¶ added in v0.3.0
type RateLimitResult struct {
// Allowed says whether to serve the request.
Allowed bool
// Remaining is how much budget is left after this attempt.
Remaining int
// RetryAfter is how long until the caller may try again. It is only
// meaningful when Allowed is false.
RetryAfter time.Duration
// Reset is when the budget returns to full. Zero when the store does not
// track it.
Reset time.Time
}
RateLimitResult is one store decision.
type RateLimitStore ¶ added in v0.3.0
type RateLimitStore interface {
// Take records an attempt against key and reports whether it is allowed.
// limit and window describe the budget; a store keyed by more than one
// budget can use them to size the bucket it creates.
Take(ctx context.Context, key string, limit int, window time.Duration) (RateLimitResult, error)
}
RateLimitStore decides whether one more request may be served for a key.
It is an interface because the useful implementations are not the library's to write: a limit that holds across several instances of a service needs something both instances can see, and that means a dependency this package will not take. The shape here is what a shared store can answer efficiently - one call that both decides and reports what is left.
type RealIPOption ¶
type RealIPOption func(*realIPConfig)
RealIPOption configures the RealIP middleware.
func TrustLoopbackProxies ¶
func TrustLoopbackProxies() RealIPOption
TrustLoopbackProxies trusts the loopback ranges (127.0.0.0/8 and ::1/128), which is convenient for local development behind a reverse proxy.
func WithTrustedProxies ¶
func WithTrustedProxies(cidrs ...string) RealIPOption
WithTrustedProxies marks the given CIDR ranges as trusted proxies. Forwarded headers are honored only when the direct peer (RemoteAddr) falls inside one of them. Invalid CIDRs are ignored.
type RecovererOption ¶
type RecovererOption func(*recovererConfig)
RecovererOption configures the Recoverer middleware.
func WithRecoverHandler ¶
func WithRecoverHandler(fn func(http.ResponseWriter, *http.Request, any)) RecovererOption
WithRecoverHandler sets a handler that renders the response after a panic. It runs only when nothing has been written to the client yet; the recovered value is passed as the third argument. Without it, a bare 500 is sent on an uncommitted response.
func WithRecoverLogger ¶
func WithRecoverLogger(l *slog.Logger) RecovererOption
WithRecoverLogger sets the logger used to report a recovered panic. When nil (the default), slog.Default is used.
type RequestIDOption ¶
type RequestIDOption func(*requestIDConfig)
RequestIDOption configures the RequestID middleware.
func WithRequestIDGenerator ¶
func WithRequestIDGenerator(fn func() string) RequestIDOption
WithRequestIDGenerator sets the function that produces a new identifier.
func WithRequestIDHeader ¶
func WithRequestIDHeader(name string) RequestIDOption
WithRequestIDHeader sets the header used to read and write the identifier.
func WithTrustedRequestIDHeader ¶
func WithTrustedRequestIDHeader() RequestIDOption
WithTrustedRequestIDHeader lets RequestID reuse a client-supplied identifier when it is safe (ASCII, no control characters, within the length limit). Without this option a fresh identifier is always generated.
type SecurityOption ¶
type SecurityOption func(*securityConfig)
SecurityOption configures the SecurityHeaders middleware.
func WithCSP ¶
func WithCSP(policy string) SecurityOption
WithCSP sets a Content-Security-Policy. It is opt-in because a good policy depends on the application.
func WithFrameOptions ¶
func WithFrameOptions(v string) SecurityOption
WithFrameOptions sets the X-Frame-Options value (default "DENY"). An empty string omits the header.
func WithHSTS ¶
func WithHSTS(seconds int) SecurityOption
WithHSTS enables Strict-Transport-Security with the given max-age in seconds. It should only be set for sites served exclusively over HTTPS. Zero, the default, omits the header.
Use WithHSTSPolicy for the directives a full policy usually carries.
func WithHSTSPolicy ¶ added in v0.3.0
func WithHSTSPolicy(p HSTS) SecurityOption
WithHSTSPolicy sets a full Strict-Transport-Security policy, including includeSubDomains and preload.
It replaces rather than extends WithHSTS; the last of the two applied wins. A zero MaxAge omits the header, so a policy can be turned off in one place without unpicking the rest of it.
func WithReferrerPolicy ¶
func WithReferrerPolicy(v string) SecurityOption
WithReferrerPolicy sets the Referrer-Policy value (default "no-referrer"). An empty string omits the header.
type ThrottleOption ¶
type ThrottleOption func(*throttleConfig)
ThrottleOption configures the Throttle middleware.
func WithThrottleRetryAfter ¶
func WithThrottleRetryAfter(seconds int) ThrottleOption
WithThrottleRetryAfter sets the Retry-After header (in seconds) on a rejected request. Zero, the default, omits the header.
func WithThrottleStatus ¶
func WithThrottleStatus(code int) ThrottleOption
WithThrottleStatus sets the status returned when the limit is reached (default 503 Service Unavailable).
type TimeoutOption ¶
type TimeoutOption func(*timeoutConfig)
TimeoutOption configures the Timeout middleware.
func WithTimeoutMessage ¶
func WithTimeoutMessage(msg string) TimeoutOption
WithTimeoutMessage sets the body sent when the deadline is exceeded.