servex

package module
v2.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 9, 2025 License: MIT Imports: 46 Imported by: 1

README

Servex - Production-Ready HTTP Server for Go

Go Version GoDoc Build Coverage GoReport MIT

Servex eliminates HTTP server boilerplate in Go. Focus on business logic while getting production-ready features out of the box.

Features

  • 🚀 Zero Boilerplate - Configure once, code business logic
  • 🔒 Security First - JWT auth, rate limiting, request filtering, security headers, audit logging
  • 🔄 Reverse Proxy / API Gateway - Load balancing, health checks, traffic analysis
  • Native Compatibility - Works seamlessly with existing net/http code

Quick Start

go get -u github.com/maxbolgarin/servex/v2
Basic Server
server, _ := servex.New(servex.ProductionPreset()...)
server.HandleFunc("/hello", helloHandler)
server.StartWithWaitSignals(context.Background(), ":8080", "")
Context Helper
func handler(w http.ResponseWriter, r *http.Request) {
    ctx := servex.C(w, r)

    request, err := servex.ReadJSON[Request](r)
    if err != nil {
        ctx.BadRequest(err, "invalid request")
        return
    }

    ctx.Response(http.StatusOK, response)
}

Presets

Quick configurations for common scenarios:

Preset Use Case
DevelopmentPreset() Development with minimal setup
ProductionPreset() Production with security, rate limiting, monitoring
APIServerPreset() REST APIs
WebAppPreset() Web applications with security headers
MicroservicePreset() Fast timeouts, minimal security
HighSecurityPreset() Maximum security features
QuickTLSPreset(cert, key) Production + SSL

Features & Configuration

Feature Configuration Options
Authentication WithAuth(config) - Full JWT auth with database
WithAuthToken(token) - Simple bearer token
WithAuthMemoryDatabase() - In-memory user storage
WithAuthKey(accessKey, refreshKey) - Custom JWT keys
WithAuthTokensDuration(access, refresh) - Token lifetimes
WithAuthIssuer(issuer) - JWT issuer
WithAuthBasePath(path) - Auth routes prefix
WithAuthInitialRoles(roles...) - Default user roles
Rate Limiting WithRPM(requests) - Requests per minute
WithRPS(requests) - Requests per second
WithRequestsPerInterval(requests, interval) - Custom interval
WithBurstSize(size) - Burst allowance
WithRateLimitConfig(config) - Full configuration
WithRateLimitExcludePaths(paths...) - Exclude paths
WithRateLimitIncludePaths(paths...) - Include only paths
Request Filtering WithBlockedIPs(ips...) - Block IP ranges
WithAllowedIPs(ips...) - Allow only IPs
WithBlockedUserAgents(agents...) - Block user agents
WithBlockedUserAgentsRegex(patterns...) - Block by regex
WithAllowedHeaders(headers) - Allow headers
WithBlockedHeaders(headers) - Block headers
WithAllowedQueryParams(params) - Allow query params
WithBlockedQueryParams(params) - Block query params
WithFilterConfig(config) - Full configuration
Security Headers WithSecurityHeaders() - Basic headers
WithStrictSecurityHeaders() - Strict CSP, HSTS
WithContentSecurityPolicy(policy) - Custom CSP
WithHSTSHeader(maxAge, includeSubdomains, preload) - HSTS config
WithSecurityConfig(config) - Full configuration
CSRF Protection WithCSRFProtection() - Enable CSRF
WithCSRFTokenName(name) - Token header name
WithCSRFCookieName(name) - Cookie name
WithCSRFCookieHttpOnly(httpOnly) - HttpOnly flag
WithCSRFCookieSecure(secure) - Secure flag
WithCSRFTokenEndpoint(endpoint) - Token endpoint
CORS WithCORS() - Enable with defaults
WithCORSAllowOrigins(origins...) - Allowed origins
WithCORSAllowMethods(methods...) - Allowed methods
WithCORSAllowHeaders(headers...) - Allowed headers
WithCORSAllowCredentials() - Allow credentials
WithCORSMaxAge(seconds) - Preflight cache
WithCORSConfig(config) - Full configuration
Compression WithCompression() - Enable gzip
WithCompressionLevel(level) - Level 1-9
WithCompressionMinSize(size) - Min size to compress
WithCompressionTypes(types...) - Content types
WithCompressionConfig(config) - Full configuration
Caching WithCachePublic(maxAge) - Public cache
WithCachePrivate(maxAge) - Private cache
WithCacheStaticAssets(maxAge) - Static file cache
WithCacheNoCache() - Disable caching
WithCacheControl(control) - Custom Cache-Control
WithCacheConfig(config) - Full configuration
Static Files / SPA WithStaticFiles(dir, prefix) - Serve static files
WithSPAMode(dir, indexFile) - SPA mode
WithStaticFileConfig(config) - Full configuration
WithStaticFileCache(maxAge, rules...) - Cache rules
Reverse Proxy WithProxyConfig(config) - Full proxy configuration
Supports load balancing, health checks, path rewriting
HTTPS / TLS WithCertificate(cert) - TLS certificate
WithCertificateFromFile(cert, key) - Load from files
WithHTTPSRedirect() - Redirect HTTP to HTTPS
WithHTTPSRedirectTemporary() - 307 redirect
WithHTTPSRedirectConfig(config) - Full redirect config
Logging & Monitoring WithLogger(logger) - Custom logger
WithDefaultAuditLogger() - Security audit logs
WithAuditLogger(logger) - Custom audit logger
WithDisableRequestLogging() - Disable request logs
WithNoLogClientErrors() - Skip 4xx errors
WithLogFields(fields...) - Additional log fields
Health & Metrics WithHealthEndpoint() - Enable /health
WithHealthPath(path) - Custom health path
WithDefaultMetrics(path) - Prometheus metrics
WithMetrics(metrics) - Custom metrics
WithDisableHealthEndpoint() - Disable health
Server Timeouts WithReadTimeout(duration) - Request read timeout
WithReadHeaderTimeout(duration) - Header read timeout
WithIdleTimeout(duration) - Keep-alive timeout
WithMaxHeaderBytes(size) - Max header size
Request Size Limits WithMaxRequestBodySize(size) - Max body size
WithMaxJSONBodySize(size) - Max JSON size
WithMaxFileUploadSize(size) - Max file size
WithMaxMultipartMemory(size) - Multipart memory
WithRequestSizeLimits() - Enable defaults
WithStrictRequestSizeLimits() - Strict limits

Core Features

Authentication

Built-in JWT authentication with user management:

server, _ := servex.New(
    servex.WithAuth(servex.AuthConfig{
        Database:            authDB,
        RolesOnRegister:     []servex.UserRole{"user"},
        AccessTokenDuration: 15 * time.Minute,
    }),
)

// Auto-registers: /auth/login, /auth/register, /auth/refresh, /auth/logout
server.HandleFuncWithAuth("/admin", adminHandler, "admin")
Rate Limiting

Protect all your APIs:

server, _ := servex.New(servex.WithRPM(100)) // 100 requests per minute

Or per-endpoint limits via register after server creation:

locationConfigs := []servex.LocationRateLimitConfig{
    {
        PathPatterns: []string{"/auth/login"},
        Config: servex.RateLimitConfig{
            RequestsPerInterval: 5,
            Interval: time.Minute,
        },
    },
}
servex.RegisterLocationBasedRateLimitMiddleware(server.Router(), locationConfigs)
Request Filtering

Block malicious traffic:

server, _ := servex.New(
    servex.WithBlockedIPs("192.0.2.0/24"),
    servex.WithBlockedUserAgentsRegex(`(?i).*(bot|crawler).*`),
)
Reverse Proxy

L7 reverse proxy with load balancing:

proxyConfig := servex.ProxyConfiguration{
    Enabled: true,
    Rules: []servex.ProxyRule{
        {
            PathPrefix: "/api/",
            Backends: []servex.Backend{
                {URL: "http://backend1:8080", Weight: 2},
                {URL: "http://backend2:8080", Weight: 1},
            },
            LoadBalancing: servex.WeightedRoundRobinStrategy,
            StripPrefix:   "/api",
        },
    },
}

server, _ := servex.New(servex.WithProxyConfig(proxyConfig))
Security Headers
server, _ := servex.New(
    servex.WithStrictSecurityHeaders(), // CSP, HSTS, etc.
    servex.WithCSRFProtection(),
)
Audit Logging

Track security events:

server, _ := servex.New(
    servex.WithDefaultAuditLogger(),
    servex.WithAuth(authConfig),
)
// Logs: auth events, rate limits, filter blocks, CSRF violations

Context Helpers

ctx := servex.C(w, r)

// Request
requestID := ctx.RequestID()
apiVersion := ctx.APIVersion()      // Extracts 'v1' from /api/v1/...
userID := ctx.Path("id")            // Path parameter
sort := ctx.Query("sort")           // Query parameter
user, _ := servex.ReadJSON[User](r)

// Response
ctx.Response(http.StatusOK, data)
ctx.BadRequest(err, "invalid input")
ctx.NotFound(err, "not found")
ctx.InternalServerError(err, "error")

// Cookies
cookie, _ := ctx.Cookie("session")
ctx.SetCookie("session", "token", 3600, true, true)

Configuration Options

Servex provides 100+ options via With... pattern:

server, _ := servex.New(
    // Server
    servex.WithReadTimeout(30*time.Second),
    servex.WithLogger(slog.Default()),

    // Security
    servex.WithStrictSecurityHeaders(),
    servex.WithRPM(1000),
    servex.WithBlockedIPs("10.0.0.0/8"),

    // Features
    servex.WithHealthEndpoint(),
    servex.WithDefaultMetrics(),
    servex.WithCompression(),
    servex.WithCORS(),

    // Static Files / SPA
    servex.WithSPAMode("build", "index.html"),
)

See Complete Configuration Reference for all options.

HTTP Method Shortcuts

server.GET("/users", listUsers)
server.POST("/users", createUser)
server.PUT("/users/{id}", updateUser)
server.DELETE("/users/{id}", deleteUser)

// With authentication
server.GetWithAuth("/admin", adminHandler, "admin")
server.PostWithAuth("/api/data", dataHandler, "user")

Server Start Options

// 1. Basic start (non-blocking)
server.Start(":8080", ":8443")

// 2. With automatic shutdown on context cancel
server.StartWithShutdown(ctx, ":8080", "")

// 3. Wait for signals (Ctrl+C) - blocking
server.StartWithWaitSignals(ctx, ":8080", "")

// 4. Separate HTTP/HTTPS
server.StartHTTP(":8080")
server.StartHTTPS(":8443")

Complete Configuration Reference

View all configuration options (100+)
TLS & Certificates
  • WithCertificate(cert) - Set TLS certificate
  • WithCertificateFromFile(cert, key) - Load from files
Timeouts
  • WithReadTimeout(duration) - Request read timeout
  • WithIdleTimeout(duration) - Keep-alive timeout
Authentication
  • WithAuth(config) - JWT authentication
  • WithAuthMemoryDatabase() - In-memory user database
  • WithAuthToken(token) - Simple bearer token
  • WithAuthTokensDuration(access, refresh) - Token lifetimes
Rate Limiting
  • WithRPM(requests) - Requests per minute
  • WithRPS(requests) - Requests per second
  • WithBurstSize(size) - Burst allowance
  • WithRateLimitKeyFunc(func) - Custom rate limit key
Request Filtering
  • WithBlockedIPs(ips...) - Block IP ranges
  • WithAllowedIPs(ips...) - Allow only specific IPs
  • WithBlockedUserAgents(agents...) - Block user agents
  • WithBlockedUserAgentsRegex(patterns...) - Block by regex
Security
  • WithSecurityHeaders() - Basic security headers
  • WithStrictSecurityHeaders() - Strict CSP, HSTS
  • WithCSRFProtection() - CSRF token validation
  • WithContentSecurityPolicy(policy) - Custom CSP
CORS
  • WithCORS() - Enable with defaults
  • WithCORSAllowOrigins(origins...) - Allowed origins
  • WithCORSAllowMethods(methods...) - Allowed methods
  • WithCORSAllowCredentials() - Allow credentials
Caching
  • WithCachePublic(maxAge) - Public cache
  • WithCachePrivate(maxAge) - Private cache
  • WithCacheStaticAssets(maxAge) - Cache static files
  • WithCacheNoCache() - Disable caching
Logging & Monitoring
  • WithLogger(logger) - Custom logger
  • WithDefaultAuditLogger() - Security audit logging
  • WithHealthEndpoint() - Health check endpoint
  • WithDefaultMetrics() - Prometheus metrics
Performance
  • WithCompression() - Gzip compression
  • WithCompressionLevel(level) - Compression level (1-9)
  • WithMaxRequestBodySize(size) - Limit request size
Static Files
  • WithStaticFiles(dir, prefix) - Serve static files
  • WithSPAMode(dir, index) - Single Page Application mode
Reverse Proxy
  • WithProxyConfig(config) - Complete proxy configuration

Why Servex?

Before Servex
func handler(w http.ResponseWriter, r *http.Request) {
    bodyBytes, _ := io.ReadAll(r.Body)
    var request Request
    json.Unmarshal(bodyBytes, &request)

    respBytes, _ := json.Marshal(resp)
    w.Header().Set("Content-Type", "application/json")
    w.Write(respBytes)
}
With Servex
func handler(w http.ResponseWriter, r *http.Request) {
    ctx := servex.C(w, r)
    request, _ := servex.ReadJSON[Request](r)
    ctx.Response(http.StatusOK, resp)
}

Examples

See examples/ for complete working examples:

  • Basic server
  • Authentication
  • Rate limiting
  • Reverse proxy
  • SPA serving

Contributing

Pull requests and issues welcome! See LICENSE for terms.

License

MIT License - see LICENSE file.


Documentation

Overview

Package servex provides a basic HTTP(S) server based on a net/http and gorilla/mux.

Index

Examples

Constants

View Source
const (
	IDDBField                    = "id"
	IDDBBsonField                = "_id"
	UsernameDBField              = "username"
	RolesDBField                 = "roles"
	PasswordHashDBField          = "password_hash"
	RefreshTokenHashDBField      = "refresh_token_hash"
	RefreshTokenExpiresAtDBField = "refresh_token_expires_at"
)
View Source
const (
	// RequestIDLogField adds request ID to logs.
	RequestIDLogField = "request_id"
	// IPLogField adds remote IP address to logs.
	IPLogField = "ip"
	// UserAgentLogField adds client's User-Agent to logs.
	UserAgentLogField = "user_agent"
	// URLLogField adds request URL to logs.
	URLLogField = "url"
	// MethodLogField adds request method to logs like GET or POST.
	MethodLogField = "method"
	// ProtoLogField adds request protocol to logs like HTTP/1.1 or HTTP/2.
	ProtoLogField = "proto"
	// ErrorLogField adds error information to logs.
	ErrorLogField = "error"
	// ErrorMessageLogField adds error message to logs.
	ErrorMessageLogField = "error_message"
	// StatusLogField adds HTTP status code to logs.
	StatusLogField = "status"
	// DurationLogField adds request duration in milliseconds to logs.
	DurationLogField = "duration_ms"
)
View Source
const (
	// GET is the HTTP GET method.
	GET = http.MethodGet

	// HEAD is the HTTP HEAD method.
	HEAD = http.MethodHead

	// POST is the HTTP POST method.
	POST = http.MethodPost

	// PUT is the HTTP PUT method.
	PUT = http.MethodPut

	// PATCH is the HTTP PATCH method.
	PATCH = http.MethodPatch

	// DELETE is the HTTP DELETE method.
	DELETE = http.MethodDelete

	// CONNECT is the HTTP CONNECT method.
	CONNECT = http.MethodConnect

	// OPTIONS is the HTTP OPTIONS method.
	OPTIONS = http.MethodOptions

	// TRACE is the HTTP TRACE method.
	TRACE = http.MethodTrace
)

HTTP method shortcuts HTTP methods shortcuts

View Source
const (
	// MIMETypeAAC defines the MIME type for AAC audio.
	MIMETypeAAC = "audio/aac"

	// MIMETypeABW defines the MIME type for AbiWord documents.
	MIMETypeABW = "application/x-abiword"

	// MIMETypeAPNG defines the MIME type for Animated Portable Network Graphics (APNG).
	MIMETypeAPNG = "image/apng"

	// MIMETypeARC defines the MIME type for Archive documents (multiple files embedded).
	MIMETypeARC = "application/x-freearc"

	// MIMETypeAVIF defines the MIME type for AVIF images.
	MIMETypeAVIF = "image/avif"

	// MIMETypeAVI defines the MIME type for AVI (Audio Video Interleave).
	MIMETypeAVI = "video/x-msvideo"

	// MIMETypeAZW defines the MIME type for Amazon Kindle eBook format.
	MIMETypeAZW = "application/vnd.amazon.ebook"

	// MIMETypeBIN defines the MIME type for any kind of binary data.
	MIMETypeBIN = "application/octet-stream"

	// MIMETypeBMP defines the MIME type for Windows OS/2 Bitmap Graphics.
	MIMETypeBMP = "image/bmp"

	// MIMETypeBZ defines the MIME type for BZip archives.
	MIMETypeBZ = "application/x-bzip"

	// MIMETypeBZ2 defines the MIME type for BZip2 archives.
	MIMETypeBZ2 = "application/x-bzip2"

	// MIMETypeCDA defines the MIME type for CD audio.
	MIMETypeCDA = "application/x-cdf"

	// MIMETypeCSH defines the MIME type for C-Shell scripts.
	MIMETypeCSH = "application/x-csh"

	// MIMETypeCSS defines the MIME type for Cascading Style Sheets (CSS).
	MIMETypeCSS = "text/css"

	// MIMETypeCSV defines the MIME type for Comma-separated values (CSV).
	MIMETypeCSV = "text/csv"

	// MIMETypeDOC defines the MIME type for Microsoft Word.
	MIMETypeDOC = "application/msword"

	// MIMETypeDOCX defines the MIME type for Microsoft Word (OpenXML).
	MIMETypeDOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"

	// MIMETypeEOT defines the MIME type for MS Embedded OpenType fonts.
	MIMETypeEOT = "application/vnd.ms-fontobject"

	// MIMETypeEPUB defines the MIME type for Electronic publications (EPUB).
	MIMETypeEPUB = "application/epub+zip"

	// MIMETypeGZ defines the MIME type for GZip Compressed Archives.
	MIMETypeGZ = "application/gzip"

	// MIMETypeGIF defines the MIME type for Graphics Interchange Format (GIF).
	MIMETypeGIF = "image/gif"

	// MIMETypeHTML defines the MIME type for HyperText Markup Language (HTML).
	MIMETypeHTML = "text/html"

	// MIMETypeICO defines the MIME type for Icon format.
	MIMETypeICO = "image/vnd.microsoft.icon"

	// MIMETypeICS defines the MIME type for iCalendar format.
	MIMETypeICS = "text/calendar"

	// MIMETypeJAR defines the MIME type for Java Archives (JAR).
	MIMETypeJAR = "application/java-archive"

	// MIMETypeJPEG defines the MIME type for JPEG images.
	MIMETypeJPEG = "image/jpeg"

	// MIMETypeJS defines the MIME type for JavaScript.
	MIMETypeJS = "text/javascript"

	// MIMETypeJSON defines the MIME type for JSON format.
	MIMETypeJSON = "application/json"

	// MIMETypeJSONLD defines the MIME type for JSON-LD format.
	MIMETypeJSONLD = "application/ld+json"

	// MIMETypeMIDI defines the MIME type for Musical Instrument Digital Interface (MIDI).
	MIMETypeMIDI = "audio/midi"

	// MIMETypeMJS defines the MIME type for JavaScript modules.
	MIMETypeMJS = "text/javascript"

	// MIMETypeMP3 defines the MIME type for MP3 audio.
	MIMETypeMP3 = "audio/mpeg"

	// MIMETypeMP4 defines the MIME type for MP4 video.
	MIMETypeMP4 = "video/mp4"

	// MIMETypeMPEG defines the MIME type for MPEG Video.
	MIMETypeMPEG = "video/mpeg"

	// MIMETypeMPKG defines the MIME type for Apple Installer Packages.
	MIMETypeMPKG = "application/vnd.apple.installer+xml"

	// MIMETypeODP defines the MIME type for OpenDocument presentation documents.
	MIMETypeODP = "application/vnd.oasis.opendocument.presentation"

	// MIMETypeODS defines the MIME type for OpenDocument spreadsheet documents.
	MIMETypeODS = "application/vnd.oasis.opendocument.spreadsheet"

	// MIMETypeODT defines the MIME type for OpenDocument text documents.
	MIMETypeODT = "application/vnd.oasis.opendocument.text"

	// MIMETypeOGA defines the MIME type for Ogg audio.
	MIMETypeOGA = "audio/ogg"

	// MIMETypeOGV defines the MIME type for Ogg video.
	MIMETypeOGV = "video/ogg"

	// MIMETypeOGX defines the MIME type for Ogg.
	MIMETypeOGX = "application/ogg"

	// MIMETypeOPUS defines the MIME type for Opus audio in Ogg container.
	MIMETypeOPUS = "audio/ogg"

	// MIMETypeOTF defines the MIME type for OpenType fonts.
	MIMETypeOTF = "font/otf"

	// MIMETypePNG defines the MIME type for Portable Network Graphics.
	MIMETypePNG = "image/png"

	// MIMETypePDF defines the MIME type for Adobe Portable Document Format (PDF).
	MIMETypePDF = "application/pdf"

	// MIMETypePHP defines the MIME type for Hypertext Preprocessor (Personal Home Page).
	MIMETypePHP = "application/x-httpd-php"

	// MIMETypePPT defines the MIME type for Microsoft PowerPoint.
	MIMETypePPT = "application/vnd.ms-powerpoint"

	// MIMETypePPTX defines the MIME type for Microsoft PowerPoint (OpenXML).
	MIMETypePPTX = "application/vnd.openxmlformats-officedocument.presentationml.presentation"

	// MIMETypeRAR defines the MIME type for RAR archives.
	MIMETypeRAR = "application/vnd.rar"

	// MIMETypeRTF defines the MIME type for Rich Text Format (RTF).
	MIMETypeRTF = "application/rtf"

	// MIMETypeSH defines the MIME type for Bourne shell scripts.
	MIMETypeSH = "application/x-sh"

	// MIMETypeSVG defines the MIME type for Scalable Vector Graphics (SVG).
	MIMETypeSVG = "image/svg+xml"

	// MIMETypeTAR defines the MIME type for Tape Archives (TAR).
	MIMETypeTAR = "application/x-tar"

	// MIMETypeTIFF defines the MIME type for Tagged Image File Format (TIFF).
	MIMETypeTIFF = "image/tiff"

	// MIMETypeTS defines the MIME type for MPEG transport stream.
	MIMETypeTS = "video/mp2t"

	// MIMETypeTTF defines the MIME type for TrueType Fonts.
	MIMETypeTTF = "font/ttf"

	// MIMETypeTXT defines the MIME type for Plain Text.
	MIMETypeTXT = "text/plain"

	// MIMETypeText is an alias for MIMETypeTXT.
	MIMETypeText = MIMETypeTXT

	// MIMETypePlain is an alias for MIMETypeTXT.
	MIMETypePlain = MIMETypeTXT

	// MIMETypeVSD defines the MIME type for Microsoft Visio.
	MIMETypeVSD = "application/vnd.visio"

	// MIMETypeWAV defines the MIME type for Waveform Audio Format.
	MIMETypeWAV = "audio/wav"

	// MIMETypeWEBA defines the MIME type for WEBM audio.
	MIMETypeWEBA = "audio/webm"

	// MIMETypeWEBM defines the MIME type for WEBM video.
	MIMETypeWEBM = "video/webm"

	// MIMETypeWEBP defines the MIME type for WEBP images.
	MIMETypeWEBP = "image/webp"

	// MIMETypeWOFF defines the MIME type for Web Open Font Format (WOFF).
	MIMETypeWOFF = "font/woff"

	// MIMETypeWOFF2 defines the MIME type for Web Open Font Format (WOFF2).
	MIMETypeWOFF2 = "font/woff2"

	// MIMETypeXHTML defines the MIME type for XHTML.
	MIMETypeXHTML = "application/xhtml+xml"

	// MIMETypeXLS defines the MIME type for Microsoft Excel.
	MIMETypeXLS = "application/vnd.ms-excel"

	// MIMETypeXLSX defines the MIME type for Microsoft Excel (OpenXML).
	MIMETypeXLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"

	// MIMETypeXML defines the MIME type for XML.
	MIMETypeXML = "application/xml"

	// MIMETypeXUL defines the MIME type for XUL.
	MIMETypeXUL = "application/vnd.mozilla.xul+xml"

	// MIMETypeZIP defines the MIME type for ZIP archives.
	MIMETypeZIP = "application/zip"

	// MIMEType3GP defines the MIME type for 3GPP audio/video containers.
	MIMEType3GP = "video/3gpp"

	// MIMEType3G2 defines the MIME type for 3GPP2 audio/video containers.
	MIMEType3G2 = "video/3gpp2"

	// MIMEType7Z defines the MIME type for 7-zip archives.
	MIMEType7Z = "application/x-7z-compressed"
)

MIME type constants

Variables

View Source
var (
	// ListenAddressRegexp is used to match "ip:port" or ":port" strings or kuber domains with port.
	ListenAddressRegexp = regexp.MustCompile(`^[\w\-\/:@\.]*:[0-9]{1,5}$`)
)

Functions

func GetTLSConfig

func GetTLSConfig(cert *tls.Certificate) *tls.Config

GetTLSConfig creates a secure TLS configuration for HTTPS servers using the provided certificate. The configuration follows security best practices and enables modern TLS features.

Security features enabled:

  • TLS 1.2 minimum version (blocks older, insecure versions)
  • HTTP/2 support with ALPN negotiation
  • Server cipher suite preferences (server chooses best cipher)
  • Only secure ECDHE cipher suites (perfect forward secrecy)
  • P-256 elliptic curve preference (widely supported and secure)

Example usage:

// Load certificate
cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
	log.Fatal(err)
}

// Create secure TLS config
tlsConfig := servex.GetTLSConfig(&cert)

// Use with HTTP server
server := &http.Server{
		Addr:      ":8443",
		TLSConfig: tlsConfig,
		Handler:   myHandler,
	}
server.ListenAndServeTLS("", "") // Cert already in TLS config

Example with servex:

cert, _ := tls.LoadX509KeyPair("server.crt", "server.key")
server := servex.New(servex.WithCertificate(cert))
// GetTLSConfig is used internally by servex

Security considerations:

  • Only allows TLS 1.2+ (blocks TLS 1.0, 1.1 which have vulnerabilities)
  • Uses only ECDHE cipher suites for perfect forward secrecy
  • Prefers server cipher suite selection for optimal security
  • Enables HTTP/2 for better performance

Cipher suites included (in order of preference):

  • TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
  • TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  • TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

These cipher suites provide:

  • ECDHE: Elliptic Curve Diffie-Hellman (perfect forward secrecy)
  • AES-GCM: Authenticated encryption (confidentiality + integrity)
  • SHA256/384: Secure hash algorithms

Parameters:

  • cert: TLS certificate to use. If nil, returns nil (no TLS)

Returns:

  • *tls.Config: Secure TLS configuration, or nil if cert is nil

Note: This configuration is suitable for production use and follows current security recommendations. It may reject very old clients that don't support TLS 1.2 or modern cipher suites.

func MakeRawRequest

func MakeRawRequest(path, host string, headers map[string]string, body ...string) []byte

MakeRawRequest makes a raw HTTP request in a form of []byte.

Parameters:

  • path: The path to register the route for
  • host: The host to register the route for
  • headers: The headers to register the route for
  • body: The body to register the route for

Returns:

  • []byte: The raw HTTP request

func MakeRawResponse

func MakeRawResponse(code int, headers map[string]string, body ...string) []byte

MakeRawResponse makes a raw HTTP response in a form of []byte.

Parameters:

  • code: The code to register the route for
  • headers: The headers to register the route for
  • body: The body to register the route for

Returns:

  • []byte: The raw HTTP response

func Read

func Read(r *http.Request) ([]byte, error)

Read reads the request body with default size limit to prevent DoS attacks.

func ReadAndValidate

func ReadAndValidate[T interface{ Validate() error }](r *http.Request) (T, error)

ReadAndValidate reads a JSON from the request body to a variable of the provided type and validates it with size limits.

func ReadAndValidateWithLimit

func ReadAndValidateWithLimit[T interface{ Validate() error }](r *http.Request, maxSize int64) (T, error)

ReadAndValidateWithLimit reads and validates a JSON from the request body with a specific size limit.

func ReadCertificate

func ReadCertificate(cert, key []byte) (tls.Certificate, error)

ReadCertificate is a function that reads a TLS certificate from the given cert and key bytes and returns a tls.Certificate instance.

func ReadCertificateFromFile

func ReadCertificateFromFile(certFile, keyFile string) (tls.Certificate, error)

ReadCertificateFromFile is a function that reads a TLS certificate from the given cert and key files and returns a tls.Certificate instance.

func ReadFile

func ReadFile(r *http.Request, fileKey string) ([]byte, *multipart.FileHeader, error)

ReadFile reads a file from the request body with configurable size limits.

func ReadFileWithLimit

func ReadFileWithLimit(r *http.Request, fileKey string, maxMemory, maxFileSize int64) ([]byte, *multipart.FileHeader, error)

ReadFileWithLimit reads a file from the request body with specific size limits.

func ReadJSON

func ReadJSON[T any](r *http.Request) (T, error)

ReadJSON reads a JSON from the request body to a variable of the provided type with size limits.

func ReadJSONWithLimit

func ReadJSONWithLimit[T any](r *http.Request, maxSize int64) (T, error)

ReadJSONWithLimit reads a JSON from the request body with a specific size limit.

func ReadWithLimit

func ReadWithLimit(r *http.Request, maxSize int64) ([]byte, error)

ReadWithLimit reads the request body with a specific size limit.

func RegisterCORSMiddleware added in v2.2.0

func RegisterCORSMiddleware(router MiddlewareRouter, opts Options)

RegisterCORSMiddleware registers a middleware that handles Cross-Origin Resource Sharing (CORS). It supports preflight requests, origin validation, method and header restrictions, credentials handling, and path-based filtering. If CORS is not enabled in the configuration, no middleware is registered.

func RegisterCSRFMiddleware

func RegisterCSRFMiddleware(router MiddlewareRouter, cfg SecurityConfig)

RegisterCSRFMiddleware adds CSRF (Cross-Site Request Forgery) protection middleware. This middleware provides comprehensive CSRF protection for web applications.

func RegisterCacheControlMiddleware

func RegisterCacheControlMiddleware(router MiddlewareRouter, cfg CacheConfig)

RegisterCacheControlMiddleware adds cache control headers to HTTP responses. It implements common HTTP caching headers to control browser and proxy caching behavior. If the config is empty or disabled, no middleware will be registered.

func RegisterCompressionMiddleware added in v2.2.0

func RegisterCompressionMiddleware(router MiddlewareRouter, cfg CompressionConfig)

RegisterCompressionMiddleware adds HTTP response compression middleware. It compresses response bodies using gzip or deflate encoding based on client Accept-Encoding headers. This can significantly reduce bandwidth usage and improve response times for text-based content.

func RegisterCustomHeadersMiddleware

func RegisterCustomHeadersMiddleware(router MiddlewareRouter, customHeaders map[string]string)

RegisterCustomHeadersMiddleware adds custom headers to HTTP responses. This is separate from security headers to maintain separation of concerns.

func RegisterHTTPSRedirectMiddleware added in v2.1.0

func RegisterHTTPSRedirectMiddleware(router MiddlewareRouter, cfg HTTPSRedirectConfig)

RegisterHTTPSRedirectMiddleware adds HTTP to HTTPS redirection middleware to the router. This middleware automatically redirects all HTTP requests to their HTTPS equivalent to enforce secure connections across the entire application. If the config is disabled, no middleware will be registered.

func RegisterHeaderRemovalMiddleware

func RegisterHeaderRemovalMiddleware(router MiddlewareRouter, headersToRemove []string)

RegisterHeaderRemovalMiddleware removes specified headers from HTTP responses. Headers are removed after the handler executes to ensure proper removal of headers that might be set by the handler.

func RegisterLocationBasedRateLimitMiddleware

func RegisterLocationBasedRateLimitMiddleware(router MiddlewareRouter, locationConfigs []LocationRateLimitConfig, auditLogger ...AuditLogger) func()

RegisterLocationBasedRateLimitMiddleware adds location-based rate limiting middleware to the router. This allows different rate limit configurations for different URL paths. If no location configs are provided or none are enabled, no middleware will be registered. It returns a function that can be used to stop the cleanup routine.

The middleware will: 1. Check each location config in order for path pattern matches 2. Use the first matching config's rate limits 3. Fall back to no rate limiting if no patterns match

Example usage:

stop := RegisterLocationBasedRateLimitMiddleware(router, []LocationRateLimitConfig{
  {
    PathPatterns: []string{"/api/*"},
    Config: RateLimitConfig{
      Enabled: true,
      RequestsPerInterval: 100,
      Interval: time.Minute,
    },
  },
  {
    PathPatterns: []string{"/auth/login", "/auth/register"},
    Config: RateLimitConfig{
      Enabled: true,
      RequestsPerInterval: 10,
      Interval: time.Minute,
    },
  },
})

func RegisterLoggingMiddleware

func RegisterLoggingMiddleware(router MiddlewareRouter, logger RequestLogger, metrics Metrics, noLogClientErrors ...bool)

RegisterLoggingMiddleware registers a middleware that logs incoming requests. It logs details such as request method, path, status code, duration, and any errors encountered during processing. It also integrates with a Metrics handler if provided. If the logger is nil, it defaults to a BaseRequestLogger using slog.Default(). Requests can be excluded from logging by calling ctx.NoLog() within the handler.

func RegisterProxyMiddleware

func RegisterProxyMiddleware(router MiddlewareRouter, config ProxyConfiguration, logger ...Logger) error

RegisterProxyMiddleware registers the proxy middleware

Parameters:

  • router: The router to register the middleware for
  • config: The proxy configuration to register the middleware for
  • logger: The logger to register the middleware for (optional)

Example:

RegisterProxyMiddleware(router, config, logger)

Returns:

  • *ProxyManager: The proxy manager

func RegisterRateLimitMiddleware

func RegisterRateLimitMiddleware(router MiddlewareRouter, cfg RateLimitConfig, auditLogger ...AuditLogger) func()

RegisterRateLimitMiddleware adds rate limiting middleware to the router. If the config is not enabled, no middleware will be registered. It returns a function that can be used to stop the cleanup routine.

Parameters:

  • router: The router to register the middleware for
  • cfg: The rate limit configuration to register the middleware for
  • auditLogger: The audit logger to register the middleware for

Returns:

  • func(): The function to stop the cleanup routine

func RegisterRecoverMiddleware

func RegisterRecoverMiddleware(router MiddlewareRouter, logger ErrorLogger)

RegisterRecoverMiddleware registers a middleware that recovers from panics in HTTP handlers. If a panic occurs, it logs the error and stack trace using the provided logger (defaulting to slog.Default() if nil) and sends a 500 Internal Server Error response only if no response headers have been written yet.

func RegisterRequestSizeLimitMiddleware

func RegisterRequestSizeLimitMiddleware(router MiddlewareRouter, opts Options)

RegisterRequestSizeLimitMiddleware registers a middleware that enforces request body size limits. This helps prevent DoS attacks by limiting the maximum size of request bodies. If request size limits are not enabled in options, no middleware is registered. Requests exceeding limits are rejected with 413 Request Entity Too Large.

func RegisterSecurityHeadersMiddleware

func RegisterSecurityHeadersMiddleware(router MiddlewareRouter, cfg SecurityConfig)

RegisterSecurityHeadersMiddleware adds security headers to HTTP responses. It implements common security headers to protect against various attacks. If the config is empty or disabled, no middleware will be registered.

func RegisterSimpleAuthMiddleware

func RegisterSimpleAuthMiddleware(router MiddlewareRouter, authToken string, opts ...Options)

RegisterSimpleAuthMiddleware registers a middleware for simple token-based authentication. It checks the "Authorization" header for a token matching the provided authToken. It supports both "Bearer <token>" and "<token>" formats. If the authToken is empty, no middleware is registered. If the header is missing or the token is invalid, it responds with 401 Unauthorized.

func RegisterStaticFileMiddleware

func RegisterStaticFileMiddleware(router MiddlewareRouter, cfg StaticFileConfig)

RegisterStaticFileMiddleware sets up static file serving middleware based on the configuration. This should be called after all API routes are registered but before starting the server. It returns a cleanup function that should be called when shutting down.

func StartServer

func StartServer(cfg BaseConfig, handlerSetter func(*mux.Router), opts ...Option) (shutdown func(context.Context) error, err error)

StartServer starts the server with the provided BaseConfig and [Option]s. It returns an error if there was an error starting either of the servers. You should provide a function that sets the handlers for the server to the router. It returns shutdown function so you should shutdown the server manually.

Example:

cfg := servex.BaseConfig{
	HTTPS: ":8443",
	CertFile: "cert.pem",
	KeyFile:  "key.pem",
}

shutdown, err := servex.StartServer(cfg, func(r *mux.Router) {
	r.HandleFunc("/api/users", handleUsers)
	r.HandleFunc("/api/health", handleHealth)
}, servex.WithAuthToken("secret-token"))
if err != nil {
	log.Fatal(err)
}
defer shutdown(context.Background())

func StartServerFromConfig

func StartServerFromConfig(configFile string, handlerSetter func(*mux.Router)) (shutdown func() error, err error)

StartServerFromConfig starts a server using configuration from a YAML file

func StartServerWithShutdown

func StartServerWithShutdown(ctx context.Context, cfg BaseConfig, handlerSetter func(*mux.Router), opts ...Option) error

Start starts the server with the provided BaseConfig and [Option]s. It returns an error if there was an error starting either of the servers. You should provide a function that sets the handlers for the server to the router. It shutdowns the server when the context is closed (it starts a goroutine to check [Context.Done]).

Example:

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

cfg := servex.BaseConfig{
	HTTPS: ":8443",
	CertFile: "cert.pem",
	KeyFile:  "key.pem",
}

err := servex.StartServerWithShutdown(ctx, cfg, func(r *mux.Router) {
	r.HandleFunc("/api/users", handleUsers)
	r.HandleFunc("/api/health", handleHealth)
}, servex.WithAuthToken("secret-token"))
if err != nil {
	log.Fatal(err)
}

// Server will automatically shutdown when ctx is cancelled
// or when the context deadline is reached

Types

type AuditEvent

type AuditEvent struct {
	// Core event information
	EventType AuditEventType `json:"event_type"`
	Severity  AuditSeverity  `json:"severity"`
	Timestamp time.Time      `json:"timestamp"`
	EventID   string         `json:"event_id"`

	// Request context
	RequestID string `json:"request_id,omitempty"`
	UserID    string `json:"user_id,omitempty"`
	SessionID string `json:"session_id,omitempty"`
	ClientIP  string `json:"client_ip"`
	UserAgent string `json:"user_agent,omitempty"`
	Method    string `json:"method"`
	Path      string `json:"path"`
	Query     string `json:"query,omitempty"`
	Referer   string `json:"referer,omitempty"`

	// Security context
	BlockedRule  string            `json:"blocked_rule,omitempty"`
	RateLimitKey string            `json:"ratelimit_key,omitempty"`
	FilterType   string            `json:"filter_type,omitempty"`
	FilterValue  string            `json:"filter_value,omitempty"`
	Headers      map[string]string `json:"headers,omitempty"`

	// Event details
	Message string         `json:"message"`
	Details map[string]any `json:"details,omitempty"`
	Error   string         `json:"error,omitempty"`

	// Response information
	StatusCode   int   `json:"status_code,omitempty"`
	ResponseTime int64 `json:"response_time_ms,omitempty"`

	// Geographical information (if available)
	Country string `json:"country,omitempty"`
	Region  string `json:"region,omitempty"`
	City    string `json:"city,omitempty"`

	// Threat intelligence
	ThreatLevel  string `json:"threat_level,omitempty"`
	ThreatSource string `json:"threat_source,omitempty"`

	// Compliance tags
	ComplianceTags []string `json:"compliance_tags,omitempty"`
}

AuditEvent represents a security event that should be logged for audit purposes

type AuditEventType

type AuditEventType string

AuditEventType represents the type of security event being logged

const (
	// Authentication Events
	AuditEventAuthLoginSuccess AuditEventType = "auth.login.success"
	AuditEventAuthLoginFailure AuditEventType = "auth.login.failure"
	AuditEventAuthLogout       AuditEventType = "auth.logout"
	AuditEventAuthTokenRefresh AuditEventType = "auth.token.refresh"
	AuditEventAuthTokenInvalid AuditEventType = "auth.token.invalid"
	AuditEventAuthUnauthorized AuditEventType = "auth.unauthorized"
	AuditEventAuthForbidden    AuditEventType = "auth.forbidden"

	// Rate Limiting Events
	AuditEventRateLimitExceeded AuditEventType = "ratelimit.exceeded"
	AuditEventRateLimitBlocked  AuditEventType = "ratelimit.blocked"

	// Filtering Events
	AuditEventFilterIPBlocked     AuditEventType = "filter.ip.blocked"
	AuditEventFilterUABlocked     AuditEventType = "filter.useragent.blocked"
	AuditEventFilterHeaderBlocked AuditEventType = "filter.header.blocked"
	AuditEventFilterQueryBlocked  AuditEventType = "filter.query.blocked"

	// CSRF Events
	AuditEventCSRFTokenMissing   AuditEventType = "csrf.token.missing"
	AuditEventCSRFTokenInvalid   AuditEventType = "csrf.token.invalid"
	AuditEventCSRFAttackDetected AuditEventType = "csrf.attack.detected"

	// Request Security Events
	AuditEventSuspiciousPath   AuditEventType = "request.suspicious.path"
	AuditEventRequestTooLarge  AuditEventType = "request.too.large"
	AuditEventMaliciousPayload AuditEventType = "request.malicious.payload"

	// Admin Events
	AuditEventAdminAccess       AuditEventType = "admin.access"
	AuditEventConfigChange      AuditEventType = "config.change"
	AuditEventFilterRuleAdded   AuditEventType = "filter.rule.added"
	AuditEventFilterRuleRemoved AuditEventType = "filter.rule.removed"

	// System Events
	AuditEventSecurityViolation AuditEventType = "security.violation"
	AuditEventAnomalousActivity AuditEventType = "security.anomaly"
)

type AuditLogger

type AuditLogger interface {
	// LogSecurityEvent logs a security-related event
	LogSecurityEvent(event AuditEvent)

	// LogAuthenticationEvent logs authentication-related events
	LogAuthenticationEvent(eventType AuditEventType, r *http.Request, userID string, success bool, details map[string]any)

	// LogRateLimitEvent logs rate limiting events
	LogRateLimitEvent(r *http.Request, key string, details map[string]any)

	// LogFilterEvent logs request filtering events
	LogFilterEvent(eventType AuditEventType, r *http.Request, filterType, filterValue, rule string)

	// LogCSRFEvent logs CSRF protection events
	LogCSRFEvent(eventType AuditEventType, r *http.Request, details map[string]any)

	// LogSuspiciousActivity logs suspicious or anomalous activity
	LogSuspiciousActivity(r *http.Request, activityType string, details map[string]any)
}

AuditLogger interface for security audit logging

type AuditSeverity

type AuditSeverity string

AuditSeverity represents the severity level of an audit event

const (
	AuditSeverityLow      AuditSeverity = "low"
	AuditSeverityMedium   AuditSeverity = "medium"
	AuditSeverityHigh     AuditSeverity = "high"
	AuditSeverityCritical AuditSeverity = "critical"
)

type AuthConfig

type AuthConfig struct {
	// Enabled indicates whether authentication is enabled.
	Enabled bool

	// Database is the interface for user data persistence.
	// Must implement AuthDatabase interface for user CRUD operations.
	// Set via WithAuth() or WithAuthMemoryDatabase().
	//
	// The database handles:
	//   - User creation and retrieval
	//   - Password hashing and verification
	//   - Role management
	//   - Session tracking
	//
	// Use WithAuthMemoryDatabase() for development/testing (data is lost on restart).
	// Use WithAuth() with a persistent database implementation for production.
	Database AuthDatabase

	// JWTAccessSecret is the secret key used for signing access tokens (hex encoded).
	// Set via WithAuthKey(). If empty, a random key will be generated.
	//
	// Security requirements:
	//   - Use strong, randomly generated keys
	//   - Different from refresh token secret
	//   - Store securely (environment variables, key management systems)
	//   - Rotate periodically in production
	//
	// Example: "your-32-byte-hex-encoded-access-key"
	JWTAccessSecret string

	// JWTRefreshSecret is the secret key used for signing refresh tokens (hex encoded).
	// Set via WithAuthKey(). If empty, a random key will be generated.
	//
	// Security requirements:
	//   - Use strong, randomly generated keys
	//   - Different from access token secret
	//   - Store securely (environment variables, key management systems)
	//   - Rotate periodically in production
	//
	// Example: "your-32-byte-hex-encoded-refresh-key"
	JWTRefreshSecret string

	// AccessTokenDuration specifies the validity duration for access tokens.
	// Set via WithAuthTokensDuration(). Defaults to 5 minutes if not set.
	//
	// Recommended patterns:
	//   - Web apps: 15-60 min
	//   - APIs: 5-30 min
	//   - Mobile apps: 30-60 min
	//   - High security: 5-15 min
	//
	// Shorter tokens improve security but require more refresh operations.
	AccessTokenDuration time.Duration

	// RefreshTokenDuration specifies the validity duration for refresh tokens.
	// Set via WithAuthTokensDuration(). Defaults to 7 days if not set.
	//
	// Recommended patterns:
	//   - Web apps: 7-30 days
	//   - APIs: 1-7 days
	//   - Mobile apps: 30-90 days
	//   - High security: 1-3 days
	//
	// Longer refresh tokens improve user experience but increase security risk if compromised.
	RefreshTokenDuration time.Duration

	// IssuerNameInJWT is the issuer name included in JWT token claims.
	// This helps identify which service issued the token and can be used for validation.
	// Set via WithAuthIssuer(). Defaults to "testing" if not set.
	//
	// Use descriptive names like:
	//   - Application name: "user-service", "payment-api"
	//   - Environment-specific: "my-app-prod", "my-app-staging"
	//   - Domain-based: "api.mycompany.com"
	//
	// The issuer appears in the JWT "iss" claim and can be verified by clients.
	IssuerNameInJWT string

	// RefreshTokenCookieName is the name of the HTTP cookie used to store refresh tokens.
	// Set via WithAuthRefreshTokenCookieName(). Defaults to "_servexrt" if not set.
	//
	// Cookie characteristics:
	//   - HttpOnly: Cannot be accessed by JavaScript
	//   - Secure: Only sent over HTTPS (in production)
	//   - SameSite: Protection against CSRF attacks
	//   - Expires: Set to refresh token duration
	//
	// Choose names that don't conflict with your application's other cookies.
	RefreshTokenCookieName string

	// AuthBasePath is the base path for authentication API endpoints.
	// All auth routes will be registered under this path.
	// Set via WithAuthBasePath(). Defaults to "/api/v1/auth" if not set.
	//
	// Registered endpoints under the base path:
	//   - POST {basePath}/register
	//   - POST {basePath}/login
	//   - POST {basePath}/refresh
	//   - POST {basePath}/logout
	//   - GET {basePath}/me
	//
	// Examples: "/auth", "/api/v2/auth", "/users/auth"
	AuthBasePath string

	// RolesOnRegister are the default roles assigned to newly registered users.
	// These roles are automatically assigned when users register through the /register endpoint.
	// Set via WithAuthInitialRoles().
	//
	// Common role patterns:
	//   - Basic: ["user"]
	//   - Hierarchical: ["user", "member", "premium"]
	//   - Functional: ["reader", "writer", "admin"]
	//
	// Users can have multiple roles. Additional roles can be assigned later
	// through user management endpoints or database operations.
	RolesOnRegister []UserRole

	// InitialUsers is a list of initial users to be created when the server starts.
	// This is useful for creating admin accounts or seeding the database with test users.
	// Set via WithAuthInitialUsers().
	//
	// Security considerations:
	//   - Use strong passwords
	//   - Consider loading from environment variables
	//   - Remove or change default passwords in production
	//   - Limit to essential accounts only
	//
	// The users are created if they don't already exist in the database.
	InitialUsers []InitialUser

	// NotRegisterRoutes prevents automatic registration of default authentication routes.
	// Set to true via WithAuthNotRegisterRoutes() when you want to implement custom auth endpoints.
	//
	// When enabled, you must implement your own:
	//   - User registration endpoint
	//   - Login endpoint
	//   - Token refresh endpoint
	//   - Logout endpoint
	//   - User profile endpoint
	//
	// You can still use the AuthManager methods for token generation and validation.
	NotRegisterRoutes bool
	// contains filtered or unexported fields
}

AuthConfig holds the JWT-based authentication configuration with user management, roles, and JWT tokens. This configuration enables a complete authentication system with automatic endpoint registration.

When authentication is enabled, the following endpoints are automatically registered:

  • POST {AuthBasePath}/register - User registration
  • POST {AuthBasePath}/login - User login
  • POST {AuthBasePath}/refresh - Token refresh
  • POST {AuthBasePath}/logout - User logout
  • GET {AuthBasePath}/me - Current user info

Example configuration:

auth := AuthConfig{
	Database: myAuthDatabase,
	AccessTokenDuration: 15 * time.Minute,
	RefreshTokenDuration: 7 * 24 * time.Hour,
	AuthBasePath: "/api/v1/auth",
	IssuerNameInJWT: "my-app",
	RolesOnRegister: []UserRole{"user"},
}

type AuthConfiguration

type AuthConfiguration struct {
	Enabled                bool          `yaml:"enabled" json:"enabled" env:"SERVEX_AUTH_ENABLED"`
	JWTAccessSecret        string        `yaml:"jwt_access_secret" json:"jwt_access_secret" env:"SERVEX_AUTH_JWT_ACCESS_SECRET"`
	JWTRefreshSecret       string        `yaml:"jwt_refresh_secret" json:"jwt_refresh_secret" env:"SERVEX_AUTH_JWT_REFRESH_SECRET"`
	AccessTokenDuration    time.Duration `yaml:"access_token_duration" json:"access_token_duration" env:"SERVEX_AUTH_ACCESS_TOKEN_DURATION"`
	RefreshTokenDuration   time.Duration `yaml:"refresh_token_duration" json:"refresh_token_duration" env:"SERVEX_AUTH_REFRESH_TOKEN_DURATION"`
	Issuer                 string        `yaml:"issuer" json:"issuer" env:"SERVEX_AUTH_ISSUER"`
	RefreshTokenCookieName string        `yaml:"refresh_token_cookie_name" json:"refresh_token_cookie_name" env:"SERVEX_AUTH_REFRESH_TOKEN_COOKIE_NAME"`
	BasePath               string        `yaml:"base_path" json:"base_path" env:"SERVEX_AUTH_BASE_PATH"`
	InitialRoles           []string      `yaml:"initial_roles" json:"initial_roles" env:"SERVEX_AUTH_INITIAL_ROLES"`
	NotRegisterRoutes      bool          `yaml:"not_register_routes" json:"not_register_routes" env:"SERVEX_AUTH_NOT_REGISTER_ROUTES"`
	UseMemoryDatabase      bool          `yaml:"use_memory_database" json:"use_memory_database" env:"SERVEX_AUTH_USE_MEMORY_DATABASE"`
}

AuthConfiguration represents authentication configuration

type AuthDatabase

type AuthDatabase interface {
	// NewUser creates a new user in the database.
	NewUser(ctx context.Context, username string, passwordHash string, roles ...UserRole) (string, error)

	// FindByID finds a user by their ID.
	FindByID(ctx context.Context, id string) (user User, exists bool, err error)
	// FindByUsername finds a user by their username.
	FindByUsername(ctx context.Context, username string) (user User, exists bool, err error)
	// FindAll retrieves all users from the database.
	FindAll(ctx context.Context) ([]User, error)

	// UpdateUser updates a user's information in the database.
	// Fields are updated only if the corresponding pointers are not nil.
	UpdateUser(ctx context.Context, id string, diff *UserDiff) error
}

AuthDatabase defines the interface for interacting with the user database.

type AuthManager

type AuthManager struct {
	// contains filtered or unexported fields
}

AuthManager handles authentication and authorization logic.

func NewAuthManager

func NewAuthManager(cfg AuthConfig, auditLogger ...AuditLogger) (*AuthManager, error)

NewAuthManager creates a new AuthManager with the provided configuration. It initializes default values for configuration fields if they are not provided.

func (*AuthManager) CreateUser

func (h *AuthManager) CreateUser(ctx context.Context, username, password string, roles ...UserRole) error

CreateUser provides a programmatic way to create or update a user. If the user already exists (based on username), it updates their password and roles. If the user does not exist, it creates a new user with the provided details.

func (*AuthManager) GetAllUsersHandler

func (h *AuthManager) GetAllUsersHandler(w http.ResponseWriter, r *http.Request)

GetAllUsersHandler handles the HTTP request to retrieve all users. Note: This handler is intended for administrative purposes and might require specific roles. The corresponding route registration is commented out by default. It returns a list of all users.

func (*AuthManager) GetCurrentUserHandler

func (h *AuthManager) GetCurrentUserHandler(w http.ResponseWriter, r *http.Request)

GetCurrentUserHandler handles the HTTP request to retrieve the details of the currently authenticated user. It reads the user ID from the request context, validates it, and returns the user details.

func (*AuthManager) LoginHandler

func (h *AuthManager) LoginHandler(w http.ResponseWriter, r *http.Request)

LoginHandler handles the HTTP request for user login. It reads the request body, validates it, and logs the user in. It sets the auth cookie and returns the user login response.

func (*AuthManager) LogoutHandler

func (h *AuthManager) LogoutHandler(w http.ResponseWriter, r *http.Request)

LogoutHandler handles the HTTP request for user logout. It invalidates the refresh token associated with the current session. It sets the logout cookie and returns a no content response.

func (*AuthManager) RefreshHandler

func (h *AuthManager) RefreshHandler(w http.ResponseWriter, r *http.Request)

RefreshHandler handles the HTTP request for refreshing access tokens using a refresh token cookie. It reads the refresh token cookie, validates it, and refreshes the access token. It sets the auth cookie and returns the user login response.

func (*AuthManager) RegisterHandler

func (h *AuthManager) RegisterHandler(w http.ResponseWriter, r *http.Request)

RegisterHandler handles the HTTP request for user registration. It reads the request body, validates it, and registers a new user. It sets the auth cookie and returns the user login response.

func (*AuthManager) RegisterRoutes

func (h *AuthManager) RegisterRoutes(r *mux.Router)

RegisterRoutes registers the authentication-related HTTP routes on the provided router. It registers the following routes: - /register - POST - Register a new user - /login - POST - Login a user - /refresh - POST - Refresh an access token - /logout - POST - Logout a user - /me - GET - Get the current user

func (*AuthManager) UpdateUserRoleHandler

func (h *AuthManager) UpdateUserRoleHandler(w http.ResponseWriter, r *http.Request)

UpdateUserRoleHandler handles the HTTP request to update a user's roles. Note: This handler is intended for administrative purposes and might require specific roles. The corresponding route registration is commented out by default.

func (*AuthManager) WithAuth

func (m *AuthManager) WithAuth(next http.HandlerFunc, roles ...UserRole) http.HandlerFunc

WithAuth is an HTTP middleware that enforces authentication and authorization. It checks for a valid JWT access token in the Authorization header. If roles are provided, it verifies that the authenticated user has at least one of the required roles. The user ID and roles are added to the request context.

type Backend

type Backend struct {
	// URL is the backend server URL
	URL string `yaml:"url" json:"url"`
	// Weight for weighted load balancing (default: 1)
	Weight int `yaml:"weight" json:"weight"`
	// HealthCheckPath for health checking (optional)
	HealthCheckPath string `yaml:"health_check_path" json:"health_check_path"`
	// HealthCheckInterval for health checking (default: 30s)
	HealthCheckInterval time.Duration `yaml:"health_check_interval" json:"health_check_interval"`
	// MaxConnections limits concurrent connections to this backend (0 = unlimited)
	MaxConnections int `yaml:"max_connections" json:"max_connections"`
	// contains filtered or unexported fields
}

Backend represents a backend server

type BaseConfig

type BaseConfig struct {
	// HTTP is the address to start the HTTP listener on.
	//
	// Format: "host:port" where host is optional
	// Examples:
	//   - ":8080" - Listen on all interfaces, port 8080
	//   - "localhost:8080" - Listen on localhost only
	//   - "0.0.0.0:8080" - Explicitly listen on all interfaces
	//   - "192.168.1.100:8080" - Listen on specific IP
	//
	// Leave empty to disable HTTP listener.
	HTTP string `yaml:"http" json:"http" env:"SERVER_HTTP"`

	// HTTPS is the address to start the HTTPS listener on.
	//
	// Format: "host:port" where host is optional
	// Examples:
	//   - ":8443" - Listen on all interfaces, port 8443
	//   - "localhost:8443" - Listen on localhost only
	//   - "0.0.0.0:8443" - Explicitly listen on all interfaces
	//   - "192.168.1.100:8443" - Listen on specific IP
	//
	// Requires CertFile and KeyFile to be set for TLS.
	// Leave empty to disable HTTPS listener.
	HTTPS string `yaml:"https" json:"https" env:"SERVER_HTTPS"`

	// CertFile is the path to the TLS certificate file for HTTPS.
	//
	// The file should contain the PEM-encoded certificate chain.
	// Examples:
	//   - "/etc/ssl/certs/server.crt"
	//   - "./certs/certificate.pem"
	//   - "/path/to/fullchain.pem" (Let's Encrypt style)
	//
	// Required when HTTPS is enabled.
	// Must be readable by the application.
	CertFile string `yaml:"cert_file" json:"cert_file" env:"SERVER_CERT_FILE"`

	// KeyFile is the path to the TLS private key file for HTTPS.
	//
	// The file should contain the PEM-encoded private key.
	// Examples:
	//   - "/etc/ssl/private/server.key"
	//   - "./certs/private.pem"
	//   - "/path/to/privkey.pem" (Let's Encrypt style)
	//
	// Required when HTTPS is enabled.
	// Must be readable by the application and kept secure.
	// Should have restricted file permissions (e.g., 600).
	KeyFile string `yaml:"key_file" json:"key_file" env:"SERVER_KEY_FILE"`

	// AuthToken is a simple bearer token for API authentication.
	//
	// When set, the server will check for "Authorization: Bearer <token>"
	// headers on protected routes.
	//
	// Examples:
	//   - "sk-1234567890abcdef" - API key style
	//   - "secret-development-token" - Development token
	//   - Load from environment: os.Getenv("API_SECRET")
	//
	// Security considerations:
	//   - Use strong, randomly generated tokens
	//   - Rotate tokens periodically
	//   - Never commit tokens to source control
	//   - Use environment variables in production
	//
	// For more advanced authentication, use the JWT authentication system instead.
	AuthToken string `yaml:"auth_token" json:"auth_token" env:"SERVER_AUTH_TOKEN"`
}

BaseConfig represents the base configuration for a server that can be loaded from configuration files (YAML/JSON) or environment variables. This provides a simple way to configure servers without using the functional options pattern.

The struct tags enable automatic loading from:

  • YAML files (yaml tag)
  • JSON files (json tag)
  • Environment variables (env tag)

Example YAML configuration:

# server.yaml
http: ":8080"
https: ":8443"
cert_file: "/path/to/cert.pem"
key_file: "/path/to/key.pem"
auth_token: "secret-api-key"

Example JSON configuration:

{
  "http": ":8080",
  "https": ":8443",
  "cert_file": "/path/to/cert.pem",
  "key_file": "/path/to/key.pem",
  "auth_token": "secret-api-key"
}

Example environment variables:

export SERVER_HTTP=":8080"
export SERVER_HTTPS=":8443"
export SERVER_CERT_FILE="/path/to/cert.pem"
export SERVER_KEY_FILE="/path/to/key.pem"
export SERVER_AUTH_TOKEN="secret-api-key"

Example usage:

// Load from file
var config BaseConfig
data, _ := os.ReadFile("server.yaml")
yaml.Unmarshal(data, &config)

// Validate configuration
if err := config.Validate(); err != nil {
	log.Fatal(err)
}

// Convert to servex options
var opts []servex.Option
if config.AuthToken != "" {
	opts = append(opts, servex.WithAuthToken(config.AuthToken))
}
if config.CertFile != "" && config.KeyFile != "" {
	opts = append(opts, servex.WithCertificateFromFile(config.CertFile, config.KeyFile))
}

server := servex.New(opts...)
server.Start(config.HTTP, config.HTTPS)

Use this when:

  • Loading configuration from external files
  • Using environment-based configuration
  • Deploying with container orchestration
  • Following 12-factor app principles
  • Need simple, declarative configuration

func (*BaseConfig) Validate

func (c *BaseConfig) Validate() error

Validate checks if the BaseConfig contains valid configuration values. It ensures that addresses are properly formatted and at least one listener is configured.

Validation rules:

  • At least one of HTTP or HTTPS must be set (not both empty)
  • HTTP address must match the format "host:port" if set
  • HTTPS address must match the format "host:port" if set
  • Host can be empty (defaults to all interfaces)
  • Port must be valid (1-65535)

Example valid configurations:

// HTTP only
config := BaseConfig{HTTP: ":8080"}
err := config.Validate() // nil

// HTTPS only
config := BaseConfig{
	HTTPS: ":8443",
	CertFile: "cert.pem",
	KeyFile: "key.pem",
}
err := config.Validate() // nil

// Both HTTP and HTTPS
config := BaseConfig{
	HTTP: ":8080",
	HTTPS: ":8443",
	CertFile: "cert.pem",
	KeyFile: "key.pem",
}
err := config.Validate() // nil

Example invalid configurations:

// No listeners configured
config := BaseConfig{}
err := config.Validate() // "at least one of http or https should be set"

// Invalid HTTP address format
config := BaseConfig{HTTP: "invalid-address"}
err := config.Validate() // "invalid http address"

// Invalid HTTPS address format
config := BaseConfig{
	HTTP: ":8080",
	HTTPS: "not-a-valid:address:format",
}
err := config.Validate() // "invalid https address"

Note: This method only validates address formats. It does not check:

  • Whether the ports are available
  • Whether certificate files exist or are valid
  • Whether the application has permission to bind to the ports
  • Whether the certificate and key files match

These runtime checks happen when the server actually starts.

Returns nil if the configuration is valid, or an error describing what is invalid about the configuration.

type BaseRequestLogger

type BaseRequestLogger struct {
	Logger
	// FieldsToInclude specifies which fields to include in logs.
	// If empty, all available fields will be logged (default behavior).
	// Use the exported *LogField constants to specify fields.
	FieldsToInclude []string
}

func (*BaseRequestLogger) Log

type CORSConfig added in v2.2.0

type CORSConfig struct {
	// Enabled determines whether CORS middleware is active.
	// Must be set to true for any CORS headers to be applied.
	// Set via WithCORS(), WithCORSAllowOrigins(), or WithCORSConfig().
	Enabled bool

	// AllowOrigins is a list of origins that are allowed to access the resource.
	// Origins should include the protocol, domain, and port (if not standard).
	// Set via WithCORSAllowOrigins().
	//
	// Examples:
	//   - []string{"*"}: Allow all origins (not recommended for production with credentials)
	//   - []string{"https://example.com"}: Allow specific origin
	//   - []string{"https://app.example.com", "https://admin.example.com"}: Multiple origins
	//   - []string{"http://localhost:3000", "http://localhost:8080"}: Development origins
	//
	// Security considerations:
	//   - Use specific origins instead of "*" when possible
	//   - Never use "*" with AllowCredentials: true
	//   - Include protocol (https://) and port if non-standard
	//
	// If empty, defaults to "*" (all origins allowed).
	AllowOrigins []string

	// AllowMethods is a list of HTTP methods that are allowed for cross-origin requests.
	// Set via WithCORSAllowMethods().
	//
	// Common values:
	//   - []string{GET, POST, PUT, DELETE, OPTIONS}: Full REST API
	//   - []string{GET, POST, OPTIONS}: Read and create operations
	//   - []string{GET, OPTIONS}: Read-only API
	//
	// Notes:
	//   - OPTIONS is automatically included for preflight requests
	//   - HEAD and GET are considered "simple" methods by browsers
	//   - Other methods trigger preflight requests
	//
	// If empty, defaults to common REST methods: GET, POST, PUT, DELETE, OPTIONS.
	AllowMethods []string

	// AllowHeaders is a list of headers that are allowed in cross-origin requests.
	// Set via WithCORSAllowHeaders().
	//
	// Common headers:
	//   - []string{"Content-Type", "Authorization"}: Basic API headers
	//   - []string{"Content-Type", "Authorization", "X-Requested-With"}: AJAX headers
	//   - []string{"*"}: Allow all headers (less secure)
	//
	// Standard headers that don't require explicit allowance:
	//   - Accept, Accept-Language, Content-Language
	//   - Content-Type (for simple values like text/plain, application/x-www-form-urlencoded)
	//
	// Custom headers and complex Content-Type values need explicit allowance.
	//
	// If empty, defaults to common headers: Content-Type, Authorization, X-Requested-With.
	AllowHeaders []string

	// ExposeHeaders is a list of headers that are exposed to the client.
	// These headers can be accessed by JavaScript in the browser.
	// Set via WithCORSExposeHeaders().
	//
	// Common headers to expose:
	//   - []string{"Content-Length", "Content-Range"}: File download information
	//   - []string{"X-Total-Count", "X-Page-Count"}: Pagination metadata
	//   - []string{"Location"}: Resource creation location
	//
	// By default, only simple response headers are exposed to clients.
	// Custom headers need to be explicitly listed here to be accessible.
	//
	// If empty, no additional headers are exposed beyond the defaults.
	ExposeHeaders []string

	// AllowCredentials indicates whether credentials (cookies, HTTP authentication, client-side SSL certificates)
	// are allowed to be included in cross-origin requests.
	// Set via WithCORSAllowCredentials().
	//
	// Security implications:
	//   - When true, browsers include cookies and authorization headers
	//   - Cannot use "*" for AllowOrigins when this is true
	//   - Enables authenticated cross-origin requests
	//   - Increases CSRF risk if not properly handled
	//
	// Use cases:
	//   - APIs that use session cookies for authentication
	//   - Applications with cross-origin authenticated requests
	//   - Single sign-on systems
	//
	// Default: false (safer for public APIs)
	AllowCredentials bool

	// MaxAge is the maximum number of seconds that the results of a preflight request can be cached.
	// This affects how long browsers cache CORS preflight responses.
	// Set via WithCORSMaxAge().
	//
	// Common values:
	//   - 3600: 1 hour (good for development)
	//   - 86400: 1 day (good for production)
	//   - 0: No caching (forces preflight for every request)
	//
	// Benefits of longer caching:
	//   - Fewer preflight requests (better performance)
	//   - Reduced server load
	//
	// Benefits of shorter caching:
	//   - Faster policy changes take effect
	//   - Better for development
	//
	// Default: 3600 seconds (1 hour) if not set.
	MaxAge int

	// ExcludePaths are paths that should be excluded from CORS headers.
	// Requests to these paths will not have CORS headers applied.
	// Set via WithCORSExcludePaths().
	//
	// Common exclusions:
	//   - "/internal/*": Internal endpoints not meant for browsers
	//   - "/admin/*": Admin endpoints with different CORS needs
	//   - "/webhooks/*": Webhook endpoints that don't need CORS
	//
	// Path matching supports wildcards (*) for pattern matching.
	// Use when different endpoints need different CORS policies.
	ExcludePaths []string

	// IncludePaths are paths that should have CORS headers applied.
	// If set, only requests to these paths will receive CORS headers.
	// Set via WithCORSIncludePaths().
	//
	// If both IncludePaths and ExcludePaths are set:
	//   1. Paths must match IncludePaths to receive CORS headers
	//   2. Paths in ExcludePaths are then excluded from CORS headers
	//
	// Use cases:
	//   - Apply CORS only to API endpoints: "/api/*"
	//   - CORS for specific services: "/auth/*", "/users/*"
	//   - Public endpoints only: "/public/*"
	//
	// Path matching supports wildcards (*) for pattern matching.
	// Leave empty to apply CORS to all paths (default behavior).
	IncludePaths []string
}

CORSConfig holds configuration for Cross-Origin Resource Sharing (CORS). This enables web applications running at one domain to access resources from another domain. This is essential for modern web applications, SPAs, and APIs that serve different frontends.

Example configuration:

corsConfig := CORSConfig{
	Enabled: true,
	AllowOrigins: []string{"https://example.com", "https://app.example.com"},
	AllowMethods: []string{GET, POST, PUT, DELETE, OPTIONS},
	AllowHeaders: []string{"Content-Type", "Authorization"},
	AllowCredentials: true,
	MaxAge: 3600,
}

type CORSConfiguration added in v2.2.0

type CORSConfiguration struct {
	Enabled          bool     `yaml:"enabled" json:"enabled" env:"SERVEX_CORS_ENABLED"`
	AllowOrigins     []string `yaml:"allow_origins" json:"allow_origins" env:"SERVEX_CORS_ALLOW_ORIGINS"`
	AllowMethods     []string `yaml:"allow_methods" json:"allow_methods" env:"SERVEX_CORS_ALLOW_METHODS"`
	AllowHeaders     []string `yaml:"allow_headers" json:"allow_headers" env:"SERVEX_CORS_ALLOW_HEADERS"`
	ExposeHeaders    []string `yaml:"expose_headers" json:"expose_headers" env:"SERVEX_CORS_EXPOSE_HEADERS"`
	AllowCredentials bool     `yaml:"allow_credentials" json:"allow_credentials" env:"SERVEX_CORS_ALLOW_CREDENTIALS"`
	MaxAge           int      `yaml:"max_age" json:"max_age" env:"SERVEX_CORS_MAX_AGE"`
	ExcludePaths     []string `yaml:"exclude_paths" json:"exclude_paths" env:"SERVEX_CORS_EXCLUDE_PATHS"`
	IncludePaths     []string `yaml:"include_paths" json:"include_paths" env:"SERVEX_CORS_INCLUDE_PATHS"`
}

CORSConfiguration represents CORS configuration

type CacheConfig

type CacheConfig struct {
	// Enabled determines whether cache control headers middleware is active.
	// Must be set to true for any cache control headers to be applied.
	// Set via WithCacheControl(), WithCacheHeaders(), or WithCacheConfig().
	//
	// When disabled, no cache control headers will be added to responses,
	// even if individual header values are configured.
	Enabled bool

	// CacheControl sets the Cache-Control header to control caching behavior.
	// This is the primary header for controlling HTTP caching.
	// Set via WithCacheControl() or WithCacheConfig().
	//
	// Common values:
	//   - "no-cache": Must revalidate before using cached copy
	//   - "no-store": Do not cache at all (sensitive data)
	//   - "public, max-age=3600": Public cache for 1 hour
	//   - "private, max-age=900": Private cache for 15 minutes
	//   - "public, max-age=31536000, immutable": Cache for 1 year (static assets)
	//
	// Format: "directive1, directive2, directive3=value"
	//
	// Common directives:
	//   - public/private: Who can cache
	//   - max-age=<seconds>: Cache lifetime
	//   - no-cache: Must revalidate
	//   - no-store: Never cache
	//   - must-revalidate: Revalidate when stale
	//   - immutable: Content never changes
	CacheControl string

	// Expires sets the Expires header with an absolute expiration time.
	// This provides a fallback for older HTTP/1.0 clients that don't support Cache-Control.
	// Modern clients prefer Cache-Control over Expires.
	// Set via WithCacheExpires(), WithCacheExpiresTime(), or WithCacheConfig().
	//
	// Format: HTTP date format (RFC 7231)
	// Examples:
	//   - "Wed, 21 Oct 2025 07:28:00 GMT"
	//   - Generated from time.Now().Add(duration).Format(http.TimeFormat)
	//
	// Note: If both Cache-Control max-age and Expires are present,
	// Cache-Control takes precedence in HTTP/1.1 clients.
	Expires string

	// ETag sets the ETag header for cache validation.
	// ETags allow clients to validate cached content without downloading.
	// Set via WithCacheETag() or WithCacheConfig().
	//
	// For dynamic ETags that change per request, use ETagFunc instead.
	//
	// ETag formats:
	//   - Strong ETag: `"version123"` (content identical)
	//   - Weak ETag: `W/"version123"` (content equivalent)
	//
	// Use cases:
	//   - Static files: Hash of file content
	//   - Dynamic content: Hash of data or version
	//   - APIs: Resource version or last modified timestamp
	//
	// Examples:
	//   - `"33a64df551"` (hash-based)
	//   - `"v1.2.3"` (version-based)
	//   - `W/"Tue, 15 Nov 1994 12:45:26 GMT"` (weak, timestamp-based)
	ETag string

	// ETagFunc is a function that generates ETags dynamically per request.
	// This allows for request-specific or content-specific ETags.
	// Takes precedence over the static ETag field if both are set.
	// Set via WithCacheETagFunc().
	//
	// Example:
	//   ETagFunc: func(r *http.Request) string {
	//     return `"` + getUserID(r) + "-" + getContentVersion() + `"`
	//   }
	//
	// Use cases:
	//   - User-specific content hashing
	//   - Content-based ETags (hash of response data)
	//   - Request-dependent versioning
	//   - Dynamic resource validation
	ETagFunc func(r *http.Request) string

	// LastModified sets the Last-Modified header for cache validation.
	// This indicates when the resource was last changed.
	// Set via WithCacheLastModified(), WithCacheLastModifiedTime(), or WithCacheConfig().
	//
	// For dynamic LastModified times that change per request, use LastModifiedFunc instead.
	//
	// Format: HTTP date format (RFC 7231)
	// Examples:
	//   - "Wed, 21 Oct 2015 07:28:00 GMT"
	//   - Generated from time.Format(http.TimeFormat)
	//
	// Use cases:
	//   - Static files: File modification time
	//   - Dynamic content: Data update timestamp
	//   - APIs: Resource last update time
	//
	// Benefits:
	//   - Enables conditional requests (If-Modified-Since)
	//   - Reduces bandwidth for unchanged resources
	//   - Improves cache efficiency
	LastModified string

	// LastModifiedFunc is a function that generates Last-Modified times dynamically per request.
	// This allows for request-specific or content-specific modification times.
	// Takes precedence over the static LastModified field if both are set.
	// Set via WithCacheLastModifiedFunc().
	//
	// Example:
	//   LastModifiedFunc: func(r *http.Request) time.Time {
	//     return getContentModTime(r.URL.Path)
	//   }
	//
	// Use cases:
	//   - File-based LastModified times
	//   - Database record modification times
	//   - Request-dependent timestamps
	//   - Dynamic resource validation
	LastModifiedFunc func(r *http.Request) time.Time

	// Vary sets the Vary header to specify which request headers affect caching.
	// This tells caches that the response varies based on certain request headers.
	// Set via WithCacheVary() or WithCacheConfig().
	//
	// Common values:
	//   - "Accept-Encoding": Different compression formats
	//   - "User-Agent": Different responses for different browsers
	//   - "Accept": Different content types (JSON vs XML)
	//   - "Authorization": Different responses for authenticated users
	//   - "Accept-Language": Different languages
	//
	// Multiple headers: "Accept-Encoding, User-Agent, Accept-Language"
	//
	// Use cases:
	//   - Content negotiation (compression, format, language)
	//   - User-specific content
	//   - Authentication-dependent responses
	//
	// Important: Only include headers that actually affect the response
	// to avoid cache fragmentation.
	Vary string

	// ExcludePaths are paths that should be excluded from cache control headers.
	// Requests to these paths will not have cache control headers applied.
	// Set via WithCacheExcludePaths().
	//
	// Common exclusions:
	//   - Dynamic APIs: "/api/*", "/graphql"
	//   - User-specific content: "/user/*", "/profile/*"
	//   - Authentication: "/auth/*", "/login", "/logout"
	//   - Admin interfaces: "/admin/*"
	//   - Real-time endpoints: "/ws/*", "/stream/*"
	//
	// Path matching supports wildcards (*) for pattern matching.
	// Use when different endpoints need different caching strategies.
	ExcludePaths []string

	// IncludePaths are paths that should have cache control headers applied.
	// If set, only requests to these paths will receive cache control headers.
	// Set via WithCacheIncludePaths().
	//
	// If both IncludePaths and ExcludePaths are set:
	//   1. Paths must match IncludePaths to receive cache headers
	//   2. Paths in ExcludePaths are then excluded from cache headers
	//
	// Use cases:
	//   - Cache only static assets: "/static/*", "/assets/*"
	//   - Cache specific API endpoints: "/api/public/*"
	//   - Cache documentation: "/docs/*"
	//
	// Path matching supports wildcards (*) for pattern matching.
	// Useful for applying cache headers only to specific content types.
	IncludePaths []string
}

CacheConfig represents cache control configuration for HTTP responses.

type CacheConfiguration

type CacheConfiguration struct {
	Enabled      bool     `yaml:"enabled" json:"enabled" env:"SERVEX_CACHE_ENABLED"`
	CacheControl string   `yaml:"cache_control" json:"cache_control" env:"SERVEX_CACHE_CONTROL"`
	Expires      string   `yaml:"expires" json:"expires" env:"SERVEX_CACHE_EXPIRES"`
	ETag         string   `yaml:"etag" json:"etag" env:"SERVEX_CACHE_ETAG"`
	LastModified string   `yaml:"last_modified" json:"last_modified" env:"SERVEX_CACHE_LAST_MODIFIED"`
	Vary         string   `yaml:"vary" json:"vary" env:"SERVEX_CACHE_VARY"`
	ExcludePaths []string `yaml:"exclude_paths" json:"exclude_paths" env:"SERVEX_CACHE_EXCLUDE_PATHS"`
	IncludePaths []string `yaml:"include_paths" json:"include_paths" env:"SERVEX_CACHE_INCLUDE_PATHS"`
}

CacheConfiguration represents cache control configuration

type CompressionConfig added in v2.2.0

type CompressionConfig struct {
	// Enabled determines whether response compression is active.
	// Must be set to true for compression to be applied.
	// Set via WithCompression() or WithCompressionConfig().
	Enabled bool

	// Level sets the compression level for gzip encoding.
	// Valid range: 1-9 where 1 is fastest and 9 is best compression.
	// Set via WithCompressionLevel().
	//
	// Recommended values:
	//   - 1: Fastest compression, lower CPU usage
	//   - 6: Default balance of speed and compression (recommended)
	//   - 9: Best compression, higher CPU usage
	//
	// Default: 6 if not set.
	Level int

	// MinSize is the minimum response size in bytes to trigger compression.
	// Responses smaller than this size will not be compressed.
	// Set via WithCompressionMinSize().
	//
	// Common values:
	//   - 1024: 1KB (good default)
	//   - 512: Compress smaller responses
	//   - 4096: Only compress larger responses
	//   - 0: Compress all responses regardless of size
	//
	// Default: 1024 bytes if not set.
	MinSize int

	// Types is a list of MIME types that should be compressed.
	// Only responses with these content types will be compressed.
	// Set via WithCompressionTypes().
	//
	// Common MIME types for compression:
	//   - "text/html": HTML pages
	//   - "text/css": CSS stylesheets
	//   - "application/javascript": JavaScript files
	//   - "application/json": JSON API responses
	//   - "text/xml": XML responses
	//   - "text/plain": Plain text
	//   - "image/svg+xml": SVG images
	//
	// If empty, defaults to common text-based types.
	Types []string

	// ExcludePaths are paths that should be excluded from compression.
	// Responses for these paths will not be compressed regardless of other settings.
	// Set via WithCompressionExcludePaths().
	//
	// Common exclusions:
	//   - "/api/binary/*": Binary API endpoints
	//   - "/downloads/*": File download endpoints
	//   - "/images/*": Image files (already compressed)
	//   - "/videos/*": Video files (already compressed)
	//
	// Path matching supports wildcards (*) for pattern matching.
	ExcludePaths []string

	// IncludePaths are paths that should have compression applied.
	// If set, only responses for these paths will be compressed.
	// Set via WithCompressionIncludePaths().
	//
	// If both IncludePaths and ExcludePaths are set:
	//   1. Paths must match IncludePaths to be considered for compression
	//   2. Paths in ExcludePaths are then excluded from compression
	//
	// Use cases:
	//   - Compress only API endpoints: "/api/*"
	//   - Compress only static assets: "/static/*"
	//   - Compress specific content: "/docs/*", "/help/*"
	//
	// Path matching supports wildcards (*) for pattern matching.
	// Leave empty to apply compression to all paths (default behavior).
	IncludePaths []string
}

CompressionConfig holds the HTTP response compression configuration. This configuration enables automatic compression of response bodies using gzip or deflate encoding based on client Accept-Encoding headers.

Example configuration:

compression := CompressionConfig{
	Enabled: true,
	Level: 6,
	MinSize: 1024,
	Types: []string{"text/html", "application/json", "text/css", "application/javascript"},
	ExcludePaths: []string{"/api/binary/*", "/downloads/*"},
}

type CompressionConfiguration added in v2.2.0

type CompressionConfiguration struct {
	Enabled      bool     `yaml:"enabled" json:"enabled" env:"SERVEX_COMPRESSION_ENABLED"`
	Level        int      `yaml:"level" json:"level" env:"SERVEX_COMPRESSION_LEVEL"`
	MinSize      int      `yaml:"min_size" json:"min_size" env:"SERVEX_COMPRESSION_MIN_SIZE"`
	Types        []string `yaml:"types" json:"types" env:"SERVEX_COMPRESSION_TYPES"`
	ExcludePaths []string `yaml:"exclude_paths" json:"exclude_paths" env:"SERVEX_COMPRESSION_EXCLUDE_PATHS"`
	IncludePaths []string `yaml:"include_paths" json:"include_paths" env:"SERVEX_COMPRESSION_INCLUDE_PATHS"`
}

CompressionConfiguration represents HTTP response compression configuration

type Config

type Config struct {
	// Server contains basic server configuration
	Server ServerConfig `yaml:"server" json:"server"`

	// Auth contains authentication configuration
	Auth AuthConfiguration `yaml:"auth" json:"auth"`

	// RateLimit contains rate limiting configuration
	RateLimit RateLimitConfiguration `yaml:"rate_limit" json:"rate_limit"`

	// Filter contains request filtering configuration
	Filter FilterConfiguration `yaml:"filter" json:"filter"`

	// Security contains security headers configuration
	Security SecurityConfiguration `yaml:"security" json:"security"`

	// Cache contains cache control configuration
	Cache CacheConfiguration `yaml:"cache" json:"cache"`

	// Compression contains HTTP response compression configuration
	Compression CompressionConfiguration `yaml:"compression" json:"compression"`

	// Logging contains logging configuration
	Logging LoggingConfiguration `yaml:"logging" json:"logging"`

	// CORS contains Cross-Origin Resource Sharing configuration
	CORS CORSConfiguration `yaml:"cors" json:"cors"`

	// StaticFiles contains static file serving configuration
	StaticFiles StaticFilesConfiguration `yaml:"static_files" json:"static_files"`

	// Proxy contains reverse proxy configuration
	Proxy ProxyConfiguration `yaml:"proxy" json:"proxy"`
}

Config represents a comprehensive configuration structure that can be loaded from YAML files and environment variables. It includes all major servex options.

Example YAML configuration:

# server.yaml
server:
  http: ":8080"
  https: ":8443"
  cert_file: "/path/to/cert.pem"
  key_file: "/path/to/key.pem"
  read_timeout: "30s"
  idle_timeout: "120s"
  auth_token: "secret-api-key"
  enable_health_endpoint: true
  health_path: "/health"

auth:
  enabled: true
  issuer: "my-app"
  access_token_duration: "15m"
  refresh_token_duration: "7d"
  base_path: "/api/v1/auth"
  initial_roles: ["user"]

rate_limit:
  enabled: true
  requests_per_interval: 100
  interval: "1m"
  burst_size: 20
  status_code: 429
  message: "Rate limit exceeded"

security:
  enabled: true
  content_security_policy: "default-src 'self'"
  x_frame_options: "DENY"
  strict_transport_security: "max-age=31536000"

Example environment variables:

export SERVEX_SERVER_HTTP=":8080"
export SERVEX_SERVER_HTTPS=":8443"
export SERVEX_SERVER_AUTH_TOKEN="secret-key"
export SERVEX_AUTH_ENABLED="true"
export SERVEX_RATE_LIMIT_ENABLED="true"
export SERVEX_RATE_LIMIT_REQUESTS_PER_INTERVAL="100"

func LoadConfig

func LoadConfig(filename string) (*Config, error)

LoadConfig loads configuration from a YAML file and then overlays environment variables

func LoadConfigFromEnv

func LoadConfigFromEnv() (*Config, error)

LoadConfigFromEnv loads configuration from environment variables

func LoadConfigFromFile

func LoadConfigFromFile(filename string) (*Config, error)

LoadConfigFromFile loads configuration from a YAML file

func (*Config) ToBaseConfig

func (c *Config) ToBaseConfig() BaseConfig

ToBaseConfig converts the Config to BaseConfig for simple server configuration

func (*Config) ToOptions

func (c *Config) ToOptions() ([]Option, error)

ToOptions converts the Config to servex Options

type Context

type Context struct {
	context.Context
	// contains filtered or unexported fields
}

Context provides a convenient wrapper around http.ResponseWriter and *http.Request with additional utilities for common HTTP operations.

Context simplifies common tasks such as:

  • Reading and parsing request data (JSON, files, form values)
  • Writing responses (JSON, files, error responses)
  • Extracting client information (IP, headers, cookies)
  • Managing request lifecycle (logging, error handling)

The Context is designed to be used within HTTP handlers and provides type-safe methods with built-in security features like size limits and input validation.

Example usage:

func userHandler(w http.ResponseWriter, r *http.Request) {
	ctx := servex.C(w, r)

	var user User
	if err := ctx.ReadJSON(&user); err != nil {
		ctx.BadRequest(err, "Invalid JSON")
		return
	}

	// Process user...

	ctx.JSON(map[string]string{"status": "created"})
}

func C

func C(w http.ResponseWriter, r *http.Request, opts ...Options) *Context

C creates a new Context for the HTTP request and response.

This is a convenient shortcut for NewContext() and is the most common way to create a Context in HTTP handlers.

Parameters:

  • w: The HTTP response writer
  • r: The HTTP request
  • opts: Optional server options for configuration (usually omitted in handlers)

Example:

func apiHandler(w http.ResponseWriter, r *http.Request) {
	ctx := servex.C(w, r)

	userID := ctx.Path("id")
	if userID == "" {
		ctx.BadRequest(nil, "Missing user ID")
		return
	}

	ctx.JSON(map[string]string{"user_id": userID})
}

func NewContext

func NewContext(w http.ResponseWriter, r *http.Request, optsRaw ...Options) *Context

NewContext creates a new Context for the HTTP request and response.

Parameters:

  • w: The HTTP response writer
  • r: The HTTP request
  • opts: Optional server options for configuration

Example:

func apiHandler(w http.ResponseWriter, r *http.Request) {
	ctx := servex.NewContext(w, r)

	userID := ctx.Path("id")
	if userID == "" {
		ctx.BadRequest(nil, "Missing user ID")
		return
	}

	ctx.JSON(map[string]string{"user_id": userID})
}

func (*Context) APIVersion

func (ctx *Context) APIVersion() string

APIVersion returns the API version of the handler from the path. It returns an empty string if not found. Example:

// Route definition: server.GET("/api/v1/users", handler)
// Request: GET /api/v1/users

func handler(w http.ResponseWriter, r *http.Request) {
	ctx := servex.C(w, r)
	version := ctx.APIVersion() // "v1"
}

func (*Context) BadGateway

func (ctx *Context) BadGateway(err error, msg string, fields ...any)

BadGateway handles an error by returning an HTTP error response with status code 502. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • err: The error to log and send to the client
  • msg: The error message to send to the client
  • fields: Optional fields for the error message

It is a shortcut for Context.Error.

func (*Context) BadRequest

func (ctx *Context) BadRequest(err error, msg string, fields ...any)

BadRequest handles an error by returning an HTTP error response with status code 400. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • err: The error to log and send to the client
  • msg: The error message to send to the client
  • fields: Optional fields for the error message

It is a shortcut for Context.Error.

func (*Context) Body

func (ctx *Context) Body() []byte

Body returns the request body as bytes with default size limit. Be careful with this method as it reads the entire body into memory. Use ReadWithLimit for better control over memory usage. Example:

// Request: POST /api/users
// Body: {"name": "John", "age": 30}

func handler(w http.ResponseWriter, r *http.Request) {
	ctx := servex.C(w, r)
	body := ctx.Body() // []byte(`{"name": "John", "age": 30}`)
}

func (*Context) ClientIP

func (ctx *Context) ClientIP() string

ClientIP returns the real client IP address, considering proxy headers. It checks common proxy headers in order of preference and validates IP addresses. Falls back to RemoteAddr if no valid IP is found in headers.

Headers checked (in order):

  • CF-Connecting-IP (Cloudflare)
  • True-Client-IP (Akamai, Cloudflare)
  • X-Real-IP (nginx)
  • X-Forwarded-For (first valid IP)
  • X-Client-IP
  • X-Forwarded
  • X-Cluster-Client-IP
  • Forwarded (RFC 7239)

Example:

clientIP := servex.C(w, r).ClientIP()

func (*Context) ClientIPWithTrustedProxies

func (ctx *Context) ClientIPWithTrustedProxies(trustedProxies []string) string

ClientIPWithTrustedProxies returns the real client IP address, but only trusts proxy headers if the request comes from a trusted proxy network. This is more secure when you know which proxies to trust.

Example:

trustedNets := []string{"10.0.0.0/8", "172.16.0.0/12"}
clientIP := servex.C(w, r).ClientIPWithTrustedProxies(trustedNets)

func (*Context) Conflict

func (ctx *Context) Conflict(err error, msg string, fields ...any)

Conflict handles an error by returning an HTTP error response with status code 409. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • err: The error to log and send to the client
  • msg: The error message to send to the client
  • fields: Optional fields for the error message

It is a shortcut for Context.Error.

func (*Context) Cookie

func (ctx *Context) Cookie(key string) (*http.Cookie, error)

Cookie returns the cookie with the given name.

func (*Context) Error

func (ctx *Context) Error(err error, code int, msg string, fields ...any)

Error handles errors by sending standardized HTTP error responses.

This method provides consistent error handling across your application with proper logging integration and optional client error exposure. It formats error messages and manages error context for middleware.

Features:

  • Consistent error response format
  • Integration with logging middleware
  • Configurable error exposure to clients
  • Support for formatted error messages
  • Automatic HTTP status code handling

Parameters:

  • err: The underlying error (logged but not always exposed to client)
  • code: HTTP status code (400, 401, 404, 500, etc.)
  • msg: User-friendly error message (can include format verbs)
  • fields: Optional key-value pairs for additional error context

The response format is JSON: {"message": "error description", "field1": "value1", "field2": "value2"}

Example:

// Simple error
ctx.Error(err, 400, "Invalid request")

// Formatted error message
ctx.Error(err, 404, "User not found", "user_id", userID) -> {"message": "User not found", "user_id": "123"}

// Use helper methods for common cases
ctx.BadRequest(err, "Invalid JSON payload")
ctx.NotFound(err, "Resource not found")
ctx.InternalServerError(err, "Database connection failed", "database_name", "users") -> {"message": "Database connection failed", "database_name": "users"}

Note: Do not modify the ResponseWriter after calling this method. This method should typically be followed by a return statement.

func (*Context) Forbidden

func (ctx *Context) Forbidden(err error, msg string, fields ...any)

Forbidden handles an error by returning an HTTP error response with status code 403. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • err: The error to log and send to the client
  • msg: The error message to send to the client
  • fields: Optional fields for the error message

It is a shortcut for Context.Error.

func (*Context) FormValue

func (ctx *Context) FormValue(key string) string

FormValue returns the value of the form field for the given key. Example:

// Request: POST /api/users
// Form: name=John&age=30

func handler(w http.ResponseWriter, r *http.Request) {
	ctx := servex.C(w, r)
	name := ctx.FormValue("name") // "John"
	age := ctx.FormValue("age")   // "30"
}

func (*Context) Header

func (ctx *Context) Header(key string) string

Header returns the value of the request header with the given name. If multiple values are present, they are joined with a comma and space ", ". Example:

// Request: GET /api/users
// Header: X-API-Key: abc, def

func handler(w http.ResponseWriter, r *http.Request) {
	ctx := servex.C(w, r)
	apiKey := ctx.Header("X-API-Key") // "abc, def"
}

func (*Context) InternalServerError

func (ctx *Context) InternalServerError(err error, msg string, fields ...any)

InternalServerError handles an error by returning an HTTP error response with status code 500. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • err: The error to log and send to the client
  • msg: The error message to send to the client
  • fields: Optional fields for the error message

It is a shortcut for Context.Error.

func (*Context) JSON

func (ctx *Context) JSON(bodyRaw any)

JSON is an alias for Context.Response with 200 code.

func (*Context) MethodNotAllowed added in v2.1.0

func (ctx *Context) MethodNotAllowed(fields ...any)

MethodNotAllowed handles an error by returning an HTTP error response with status code 405. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

It sends a 'Method not allowed' error response.

It is a shortcut for Context.Error.

func (*Context) NoLog

func (ctx *Context) NoLog()

NoLog marks to not log the request after returning from the handler.

func (*Context) NotAcceptable

func (ctx *Context) NotAcceptable(err error, msg string, fields ...any)

NotAcceptable handles an error by returning an HTTP error response with status code 406. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • err: The error to log and send to the client
  • msg: The error message to send to the client
  • fields: Optional fields for the error message

It is a shortcut for Context.Error.

func (*Context) NotFound

func (ctx *Context) NotFound(err error, msg string, fields ...any)

NotFound handles an error by returning an HTTP error response with status code 404. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • err: The error to log and send to the client
  • msg: The error message to send to the client
  • fields: Optional fields for the error message

It is a shortcut for Context.Error.

func (*Context) NotImplemented

func (ctx *Context) NotImplemented(err error, msg string, fields ...any)

NotImplemented handles an error by returning an HTTP error response with status code 501. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • err: The error to log and send to the client
  • msg: The error message to send to the client
  • fields: Optional fields for the error message

It is a shortcut for Context.Error.

func (*Context) ParseUnixFromQuery

func (ctx *Context) ParseUnixFromQuery(key string) (time.Time, error)

func (*Context) Path

func (ctx *Context) Path(key string) string

Path returns the value of a URL path parameter.

Path parameters are variables embedded in the URL path pattern, defined using curly braces in route definitions. They are extracted when the route matches the incoming request.

Parameters:

  • key: The name of the path parameter (without curly braces)

Returns the value extracted from the URL path, or an empty string if the parameter doesn't exist in the route.

Example:

// Route definition: server.GET("/users/{id}/posts/{postID}", handler)
// Request: GET /users/123/posts/456

func handler(w http.ResponseWriter, r *http.Request) {
	ctx := servex.C(w, r)

	userID := ctx.Path("id")       // "123"
	postID := ctx.Path("postID")   // "456"
	missing := ctx.Path("foo")     // ""

	ctx.JSON(map[string]string{
		"user_id": userID,
		"post_id": postID,
	})
}

func (*Context) PreconditionFailed added in v2.1.0

func (ctx *Context) PreconditionFailed(err error, msg string, fields ...any)

MethodConflict handles an error by returning an HTTP error response with status code 409. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • err: The error to log and send to the client
  • msg: The error message to send to the client
  • fields: Optional fields for the error message

It is a shortcut for Context.Error.

func (*Context) Query

func (ctx *Context) Query(key string) string

Query returns the value of a URL query parameter.

Query parameters are the key-value pairs that appear after the "?" in a URL. For example, in "GET /users?page=2&limit=10", this method can extract "page" and "limit" values.

Parameters:

  • key: The name of the query parameter

Returns the first value associated with the key, or an empty string if the parameter doesn't exist.

Example:

// URL: /api/users?page=2&limit=10&sort=name
page := ctx.Query("page")     // "2"
limit := ctx.Query("limit")   // "10"
sort := ctx.Query("sort")     // "name"
missing := ctx.Query("foo")   // ""

func (*Context) Read

func (ctx *Context) Read() ([]byte, error)

Read reads the request body with size limit to prevent DoS attacks. It is a shortcut for ReadWithLimit with configured default size. Example:

// Request: POST /api/users
// Body: {"name": "John", "age": 30}

func handler(w http.ResponseWriter, r *http.Request) {
	ctx := servex.C(w, r)
	body, err := ctx.Read() // []byte(`{"name": "John", "age": 30}`)
}

func (*Context) ReadAndValidate

func (ctx *Context) ReadAndValidate(body interface{ Validate() error }) error

ReadAndValidate reads a JSON from the request body to the provided variable and validates it with size limits. You should provide a pointer to the variable. Example:

type User struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}

func (u *User) Validate() error {
	if u.Name == "" {
		return errors.New("name is required")
	}
	return nil
}

func createUser(w http.ResponseWriter, r *http.Request) {
	ctx := servex.C(w, r)

	var user User
	if err := ctx.ReadAndValidate(&user); err != nil {
		ctx.BadRequest(err, "Invalid JSON payload")
		return
	}

	// Process user...
	ctx.JSON(map[string]string{"status": "created"})
}

func (*Context) ReadAndValidateWithLimit

func (ctx *Context) ReadAndValidateWithLimit(body interface{ Validate() error }, maxSize int64) error

ReadAndValidateWithLimit reads a JSON from the request body to the provided variable and validates it with size limits. You should provide a pointer to the variable. Example:

type User struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}

func (u *User) Validate() error {
	if u.Name == "" {
		return errors.New("name is required")
	}
	return nil
}

func createUser(w http.ResponseWriter, r *http.Request) {
	ctx := servex.C(w, r)

	var user User
	if err := ctx.ReadAndValidateWithLimit(&user, 1024); err != nil {
		ctx.BadRequest(err, "Invalid JSON payload")
		return
	}

	// Process user...
	ctx.JSON(map[string]string{"status": "created"})
}

func (*Context) ReadFile

func (ctx *Context) ReadFile(fileKey string) ([]byte, *multipart.FileHeader, error)

ReadFile reads a file from the request body with configurable size limits.

Parameters:

  • fileKey: The key of the file in the request

Example:

// Request: POST /api/users
// Form: file=user.txt

func handler(w http.ResponseWriter, r *http.Request) {
	ctx := servex.C(w, r)
	file, header, err := ctx.ReadFile("file") // []byte(`user.txt`), *multipart.FileHeader, nil
}

func (*Context) ReadFileWithLimit

func (ctx *Context) ReadFileWithLimit(fileKey string, maxMemory, maxFileSize int64) ([]byte, *multipart.FileHeader, error)

ReadFileWithLimit reads a file from the request body with specific size limits.

Parameters:

  • fileKey: The key of the file in the request
  • maxMemory: The maximum memory to use for the file
  • maxFileSize: The maximum size of the file

Example:

// Request: POST /api/users
// Form: file=user.txt

func handler(w http.ResponseWriter, r *http.Request) {
	ctx := servex.C(w, r)
	file, header, err := ctx.ReadFileWithLimit("file", 1024, 1024) // []byte(`user.txt`), *multipart.FileHeader, nil
}

func (*Context) ReadJSON

func (ctx *Context) ReadJSON(body any) error

ReadJSON reads and parses JSON from the request body into the provided variable.

The method automatically applies size limits to prevent DoS attacks and validates that the content is valid JSON. You must provide a pointer to the variable where the JSON should be unmarshaled.

Features:

  • Automatic size limiting (configurable via WithMaxJSONBodySize)
  • Memory-safe reading with io.LimitReader
  • Detailed error messages for debugging

Parameters:

  • body: Pointer to the variable where JSON will be unmarshaled

Example:

type User struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}

func createUser(w http.ResponseWriter, r *http.Request) {
	ctx := servex.C(w, r)

	var user User
	if err := ctx.ReadJSON(&user); err != nil {
		ctx.BadRequest(err, "Invalid JSON payload")
		return
	}

	// Process user...
	ctx.JSON(map[string]string{"status": "created"})
}

func (*Context) ReadJSONWithLimit

func (ctx *Context) ReadJSONWithLimit(body any, maxSize int64) error

ReadJSONWithLimit reads and parses JSON from the request body into the provided variable.

The method applies size limits to prevent DoS attacks and validates that the content is valid JSON. You must provide a pointer to the variable where the JSON should be unmarshaled.

Features:

  • Automatic size limiting (configurable via WithMaxJSONBodySize)
  • Memory-safe reading with io.LimitReader
  • Detailed error messages for debugging

Parameters:

  • body: Pointer to the variable where JSON will be unmarshaled

Example:

type User struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}

func createUser(w http.ResponseWriter, r *http.Request) {
	ctx := servex.C(w, r)

	var user User
	if err := ctx.ReadJSONWithLimit(&user, 1024); err != nil {
		ctx.BadRequest(err, "Invalid JSON payload")
		return
	}

	// Process user...
	ctx.JSON(map[string]string{"status": "created"})
}

func (*Context) ReadWithLimit

func (ctx *Context) ReadWithLimit(maxSize int64) ([]byte, error)

ReadWithLimit reads the request body with a specific size limit. Example:

// Request: POST /api/users
// Body: {"name": "John", "age": 30}

func handler(w http.ResponseWriter, r *http.Request) {
	ctx := servex.C(w, r)
	body, err := ctx.ReadWithLimit(1024) // []byte(`{"name": "John", "age": 30}`)
}

func (*Context) Redirect added in v2.1.0

func (ctx *Context) Redirect(url string, code int)

Redirect performs an HTTP redirect to the specified URL with the given status code. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • url: The URL to redirect to
  • code: HTTP status code (301, 302, 303, 307, 308)

Example:

// Temporary redirect
ctx.Redirect("/login", 302)

// Permanent redirect
ctx.Redirect("https://example.com/new-path", 301)

func (*Context) RedirectNotModified added in v2.1.0

func (ctx *Context) RedirectNotModified()

RedirectNotModified performs a "Not Modified" redirect (HTTP 304) to indicate that the resource has not been modified since the last request. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

This is typically used with conditional requests (If-Modified-Since, If-None-Match).

Example:

if !isModified {
	ctx.RedirectNotModified()
	return
}

func (*Context) RedirectPermanent added in v2.1.0

func (ctx *Context) RedirectPermanent(url string)

RedirectPermanent performs a permanent redirect (HTTP 301) to the specified URL. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • url: The URL to redirect to

Example:

ctx.RedirectPermanent("https://example.com/new-path")

func (*Context) RedirectPermanentPreserveMethod added in v2.1.0

func (ctx *Context) RedirectPermanentPreserveMethod(url string)

RedirectPermanentPreserveMethod performs a permanent redirect (HTTP 308) to the specified URL. Unlike 301, this guarantees that the request method and body will be preserved. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • url: The URL to redirect to

Example:

ctx.RedirectPermanentPreserveMethod("https://example.com/api/v2/users")

func (*Context) RedirectSeeOther added in v2.1.0

func (ctx *Context) RedirectSeeOther(url string)

RedirectSeeOther performs a "See Other" redirect (HTTP 303) to the specified URL. This is typically used after a POST request to redirect to a GET endpoint. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • url: The URL to redirect to

Example:

// After processing a form submission
ctx.RedirectSeeOther("/success")

func (*Context) RedirectTemporary added in v2.1.0

func (ctx *Context) RedirectTemporary(url string)

RedirectTemporary performs a temporary redirect (HTTP 302) to the specified URL. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • url: The URL to redirect to

Example:

ctx.RedirectTemporary("/login")

func (*Context) RedirectTemporaryPreserveMethod added in v2.1.0

func (ctx *Context) RedirectTemporaryPreserveMethod(url string)

RedirectTemporaryPreserveMethod performs a temporary redirect (HTTP 307) to the specified URL. Unlike 302, this guarantees that the request method and body will be preserved. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • url: The URL to redirect to

Example:

ctx.RedirectTemporaryPreserveMethod("/api/v2/users")

func (*Context) RedirectToHTTPS added in v2.1.0

func (ctx *Context) RedirectToHTTPS(permanent ...bool)

RedirectToHTTPS redirects the current HTTP request to its HTTPS equivalent. This method preserves the host, path, and query parameters while changing the scheme to HTTPS. It uses a permanent redirect (HTTP 301) by default to encourage browsers and search engines to update their links to use HTTPS. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • permanent: If true, uses HTTP 301 (permanent redirect). If false, uses HTTP 302 (temporary redirect)

Example:

// In a middleware or handler
if ctx.r.TLS == nil && ctx.r.Header.Get("X-Forwarded-Proto") != "https" {
	ctx.RedirectToHTTPS(true) // Permanent redirect
	return
}

// Temporary redirect (useful during testing)
ctx.RedirectToHTTPS(false)

func (*Context) RedirectToHTTPSPermanent added in v2.1.0

func (ctx *Context) RedirectToHTTPSPermanent()

RedirectToHTTPSPermanent redirects the current HTTP request to its HTTPS equivalent using a permanent redirect (HTTP 301). This is a convenience method for the most common HTTPS redirect scenario. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Example:

// In a middleware
if ctx.r.TLS == nil {
	ctx.RedirectToHTTPSPermanent()
	return
}

func (*Context) RedirectToHTTPSTemporary added in v2.1.0

func (ctx *Context) RedirectToHTTPSTemporary()

RedirectToHTTPSTemporary redirects the current HTTP request to its HTTPS equivalent using a temporary redirect (HTTP 302). This is useful during development or testing when you don't want browsers to cache the redirect. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Example:

// During development/testing
if ctx.r.TLS == nil {
	ctx.RedirectToHTTPSTemporary()
	return
}

func (*Context) RemoteAddr

func (ctx *Context) RemoteAddr() string

RemoteAddr returns the remote address of the request. This is the direct connection address and may be a proxy IP. For real client IP detection, use ClientIP() instead.

func (*Context) RequestEntityTooLarge added in v2.1.0

func (ctx *Context) RequestEntityTooLarge(err error, msg string, fields ...any)

RequestEntityTooLarge handles an error by returning an HTTP error response with status code 413. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • err: The error to log and send to the client
  • msg: The error message to send to the client
  • fields: Optional fields for the error message

It is a shortcut for Context.Error.

func (*Context) RequestID

func (ctx *Context) RequestID() string

RequestID returns the request ID for the request.

func (*Context) Response

func (ctx *Context) Response(code int, bodyRaw ...any)

Response writes an HTTP response with the specified status code and optional body.

This is the primary method for sending responses. It automatically handles content type detection, header setting, and proper HTTP response formatting.

Supported body types:

  • []byte: Written directly with detected content type
  • string: Written as text/plain with UTF-8 charset
  • any other type: Marshaled to JSON with application/json content type
  • nil: Sends only status code with no body

Features:

  • Automatic Content-Type header setting
  • Content-Length header calculation
  • JSON marshaling with error handling
  • Memory-efficient for large responses

Parameters:

  • code: HTTP status code (e.g., 200, 404, 500)
  • bodyRaw: Optional response body (supports multiple types)

Example:

// JSON response
ctx.Response(200, map[string]string{"message": "success"})

// String response
ctx.Response(200, "Hello, World!")

// Byte response (e.g., file content)
ctx.Response(200, fileBytes)

// Status-only response
ctx.Response(204)

Note: Do not modify the ResponseWriter after calling this method. This method should typically be the last operation in your handler.

func (*Context) ResponseFile

func (ctx *Context) ResponseFile(filename string, mimeType string, body []byte)

ResponseFile writes the file to the http.ResponseWriter. It sets the Content-Type header to the provided mime type. It sets the Content-Disposition header to "attachment; filename=" + filename (safely sanitized). It sets the Content-Length header to the length of the body. Parameters:

  • filename: The name of the file
  • mimeType: The mime type of the file
  • body: The body of the file

Example:

// Request: GET /api/users/123/avatar.png
// Response: 200 OK
// Content-Type: image/png
// Content-Disposition: attachment; filename="avatar.png"
// Content-Length: 12345
// Body: file content

func handler(w http.ResponseWriter, r *http.Request) {
	ctx := servex.C(w, r)
	ctx.ResponseFile("avatar.png", "image/png", fileBytes)
}

func (*Context) ServiceUnavailable

func (ctx *Context) ServiceUnavailable(err error, msg string, fields ...any)

ServiceUnavailable handles an error by returning an HTTP error response with status code 503. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • err: The error to log and send to the client
  • msg: The error message to send to the client
  • fields: Optional fields for the error message

It is a shortcut for Context.Error.

func (*Context) SetContentType

func (ctx *Context) SetContentType(mimeType string, charset ...string)

SetContentType sets the Content-Type header.

func (*Context) SetCookie

func (ctx *Context) SetCookie(name, value string, maxAge int, secure, httpOnly bool)

SetCookie sets the cookie with the given name, value, maxAge, secure and httpOnly. maxAge is the time in seconds until the cookie expires. If maxAge < 0, the cookie is deleted. secure specifies if the cookie should only be transmitted over HTTPS. httpOnly prevents the cookie from being accessed through JavaScript, enhancing security against XSS attacks.

func (*Context) SetDeleteCookie

func (ctx *Context) SetDeleteCookie(name string)

SetDeleteCookie sets the cookie with the given name to be deleted.

func (*Context) SetHeader

func (ctx *Context) SetHeader(key string, value ...string)

SetHeader sets the value of the response header with the given name. If multiple values are provided, they are added to the header.

func (*Context) SetRawCookie

func (ctx *Context) SetRawCookie(c *http.Cookie)

SetRawCookie sets the cookie with the given http.Cookie.

func (*Context) TooManyRequests

func (ctx *Context) TooManyRequests(err error, msg string, fields ...any)

TooManyRequests handles an error by returning an HTTP error response with status code 429. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • err: The error to log and send to the client
  • msg: The error message to send to the client
  • fields: Optional fields for the error message

It is a shortcut for Context.Error.

func (*Context) Unauthorized

func (ctx *Context) Unauthorized(err error, msg string, fields ...any)

Unauthorized handles an error by returning an HTTP error response with status code 401. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • err: The error to log and send to the client
  • msg: The error message to send to the client
  • fields: Optional fields for the error message

It is a shortcut for Context.Error.

func (*Context) UnprocessableEntity

func (ctx *Context) UnprocessableEntity(err error, msg string, fields ...any)

StatusUnprocessableEntity handles an error by returning an HTTP error response with status code 422. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • err: The error to log and send to the client
  • msg: The error message to send to the client
  • fields: Optional fields for the error message

It is a shortcut for Context.Error.

func (*Context) UnsupportedMediaType added in v2.1.0

func (ctx *Context) UnsupportedMediaType(err error, msg string, fields ...any)

UnsupportedMediaType handles an error by returning an HTTP error response with status code 415. You should not modify the http.ResponseWriter after calling this method. You will probably want to return from your handler after calling this method.

Parameters:

  • err: The error to log and send to the client
  • msg: The error message to send to the client
  • fields: Optional fields for the error message

It is a shortcut for Context.Error.

type DefaultAuditLogger

type DefaultAuditLogger struct {
	Logger Logger

	// Configuration for audit logging
	IncludeHeaders    bool
	SensitiveHeaders  []string // Headers to exclude from logging for privacy
	MaxDetailSize     int      // Maximum size of detail fields
	EnableGeoLocation bool     // Enable geographical context (requires external service)
}

DefaultAuditLogger implements AuditLogger using the standard logger interface

func NewDefaultAuditLogger

func NewDefaultAuditLogger(logger Logger) *DefaultAuditLogger

NewDefaultAuditLogger creates a new default audit logger

func (*DefaultAuditLogger) LogAuthenticationEvent

func (al *DefaultAuditLogger) LogAuthenticationEvent(eventType AuditEventType, r *http.Request, userID string, success bool, details map[string]any)

LogAuthenticationEvent logs authentication-related events

func (*DefaultAuditLogger) LogCSRFEvent

func (al *DefaultAuditLogger) LogCSRFEvent(eventType AuditEventType, r *http.Request, details map[string]any)

LogCSRFEvent logs CSRF protection events

func (*DefaultAuditLogger) LogFilterEvent

func (al *DefaultAuditLogger) LogFilterEvent(eventType AuditEventType, r *http.Request, filterType, filterValue, rule string)

LogFilterEvent logs request filtering events

func (*DefaultAuditLogger) LogRateLimitEvent

func (al *DefaultAuditLogger) LogRateLimitEvent(r *http.Request, key string, details map[string]any)

LogRateLimitEvent logs rate limiting events

func (*DefaultAuditLogger) LogSecurityEvent

func (al *DefaultAuditLogger) LogSecurityEvent(event AuditEvent)

LogSecurityEvent logs a structured security event

func (*DefaultAuditLogger) LogSuspiciousActivity

func (al *DefaultAuditLogger) LogSuspiciousActivity(r *http.Request, activityType string, details map[string]any)

LogSuspiciousActivity logs suspicious or anomalous activity

type DynamicFilterMethods

type DynamicFilterMethods interface {
	// IP Management
	AddBlockedIP(ip string) error
	RemoveBlockedIP(ip string) error
	AddAllowedIP(ip string) error
	RemoveAllowedIP(ip string) error
	IsIPBlocked(ip string) bool
	GetBlockedIPs() []string
	GetAllowedIPs() []string

	// User-Agent Management
	AddBlockedUserAgent(userAgent string) error
	RemoveBlockedUserAgent(userAgent string) error
	AddAllowedUserAgent(userAgent string) error
	RemoveAllowedUserAgent(userAgent string) error
	IsUserAgentBlocked(userAgent string) bool
	GetBlockedUserAgents() []string
	GetAllowedUserAgents() []string

	// Clear all rules
	ClearAllBlockedIPs() error
	ClearAllAllowedIPs() error
	ClearAllBlockedUserAgents() error
	ClearAllAllowedUserAgents() error
}

DynamicFilterMethods provides methods to modify filter rules at runtime. These methods are thread-safe and allow dynamic adaptation of filtering rules.

type ErrorLogger

type ErrorLogger interface {
	// Error is using to log request errors, panics, serve errors and shutodwn in StartContext errors
	Error(msg string, fields ...any)
}

type ErrorResponse

type ErrorResponse struct {
	Message string `json:"message"`
}

ErrorResponse represents a JSON for an error response.

type Filter

type Filter struct {
	// contains filtered or unexported fields
}

Filter holds the compiled filtering logic and patterns. This struct is responsible for all filtering logic.

func RegisterFilterMiddleware

func RegisterFilterMiddleware(router MiddlewareRouter, cfg FilterConfig, auditLogger ...AuditLogger) (*Filter, error)

RegisterFilterMiddleware adds request filtering middleware to the router. If the config has no filters configured, no middleware will be registered. Returns the created filter instance for dynamic modification, or nil if no filter was created.

Example
// Create a new router
router := mux.NewRouter()

// Configure filtering
filterConfig := FilterConfig{
	// Only allow requests from specific IP ranges
	AllowedIPs: []string{"192.168.1.0/24", "10.0.0.1"},

	// Block known bad bots
	BlockedUserAgents: []string{
		"BadCrawler/1.0",
	},
	BlockedUserAgentsRegex: []string{
		".*[Bb]ot.*",
		".*[Ss]pider.*",
	},

	// Require API key in specific header
	AllowedHeadersRegex: map[string][]string{
		"X-API-Key": {"key-.*"},
	},

	// Block requests with debug parameter
	BlockedQueryParams: map[string][]string{
		"debug": {"true", "1"},
	},

	// Don't filter health check endpoint
	ExcludePaths: []string{"/health"},

	// Custom response for blocked requests
	StatusCode: http.StatusUnauthorized,
	Message:    "Access denied by security policy",
}

// Register the filter middleware
RegisterFilterMiddleware(router, filterConfig)

// Add your routes
router.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Users API")
})

router.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "OK")
})

func RegisterLocationBasedFilterMiddleware

func RegisterLocationBasedFilterMiddleware(router MiddlewareRouter, locationConfigs []LocationFilterConfig, auditLogger ...AuditLogger) (*Filter, error)

RegisterLocationBasedFilterMiddleware adds location-based filtering middleware to the router. This allows different filter configurations for different URL paths. If no location configs are provided or none are enabled, no middleware will be registered.

The middleware will: 1. Check each location config in order for path pattern matches 2. Use the first matching config's filter rules 3. Fall back to no filtering if no patterns match

Example usage:

err := RegisterLocationBasedFilterMiddleware(router, []LocationFilterConfig{
  {
    PathPatterns: []string{"/api/*"},
    Config: FilterConfig{
      AllowedIPs: []string{"192.168.1.0/24"},
      StatusCode: http.StatusForbidden,
      Message:    "API access restricted",
    },
  },
  {
    PathPatterns: []string{"/admin/*"},
    Config: FilterConfig{
      AllowedIPs: []string{"10.0.0.0/8"},
      BlockedUserAgents: []string{"Bot", "Crawler"},
      StatusCode: http.StatusForbidden,
      Message:    "Admin access denied",
    },
  },
}, auditLogger)

func (*Filter) AddAllowedIP

func (f *Filter) AddAllowedIP(ip string) error

AddAllowedIP dynamically adds an IP address or CIDR range to the allowed list. This method is thread-safe and takes effect immediately.

func (*Filter) AddAllowedUserAgent

func (f *Filter) AddAllowedUserAgent(userAgent string) error

AddAllowedUserAgent dynamically adds a User-Agent string to the allowed list. This method is thread-safe and takes effect immediately.

func (*Filter) AddBlockedIP

func (f *Filter) AddBlockedIP(ip string) error

AddBlockedIP dynamically adds an IP address or CIDR range to the blocked list. This method is thread-safe and takes effect immediately.

Example:

// Block a specific IP that accessed a honeypot
err := filter.AddBlockedIP("192.168.1.100")

// Block an entire subnet
err := filter.AddBlockedIP("10.0.0.0/24")

func (*Filter) AddBlockedUserAgent

func (f *Filter) AddBlockedUserAgent(userAgent string) error

AddBlockedUserAgent dynamically adds a User-Agent string to the blocked list. This method is thread-safe and takes effect immediately.

Example:

// Block a specific bot after it accessed a honeypot
err := filter.AddBlockedUserAgent("BadBot/1.0")

func (*Filter) ClearAllAllowedIPs

func (f *Filter) ClearAllAllowedIPs() error

ClearAllAllowedIPs removes all allowed IP addresses. This method is thread-safe and takes effect immediately.

func (*Filter) ClearAllAllowedUserAgents

func (f *Filter) ClearAllAllowedUserAgents() error

ClearAllAllowedUserAgents removes all allowed User-Agent strings. This method is thread-safe and takes effect immediately.

func (*Filter) ClearAllBlockedIPs

func (f *Filter) ClearAllBlockedIPs() error

ClearAllBlockedIPs removes all blocked IP addresses. This method is thread-safe and takes effect immediately.

func (*Filter) ClearAllBlockedUserAgents

func (f *Filter) ClearAllBlockedUserAgents() error

ClearAllBlockedUserAgents removes all blocked User-Agent strings. This method is thread-safe and takes effect immediately.

func (*Filter) GetAllowedIPs

func (f *Filter) GetAllowedIPs() []string

GetAllowedIPs returns a copy of the current allowed IP list. This method is thread-safe.

func (*Filter) GetAllowedUserAgents

func (f *Filter) GetAllowedUserAgents() []string

GetAllowedUserAgents returns a copy of the current allowed User-Agent list. This method is thread-safe.

func (*Filter) GetBlockedIPs

func (f *Filter) GetBlockedIPs() []string

GetBlockedIPs returns a copy of the current blocked IP list. This method is thread-safe.

func (*Filter) GetBlockedUserAgents

func (f *Filter) GetBlockedUserAgents() []string

GetBlockedUserAgents returns a copy of the current blocked User-Agent list. This method is thread-safe.

func (*Filter) IsIPBlocked

func (f *Filter) IsIPBlocked(ip string) bool

IsIPBlocked checks if an IP address is currently blocked. This method is thread-safe.

func (*Filter) IsUserAgentBlocked

func (f *Filter) IsUserAgentBlocked(userAgent string) bool

IsUserAgentBlocked checks if a User-Agent string is currently blocked. This method is thread-safe.

func (*Filter) RemoveAllowedIP

func (f *Filter) RemoveAllowedIP(ip string) error

RemoveAllowedIP dynamically removes an IP address or CIDR range from the allowed list. This method is thread-safe and takes effect immediately.

func (*Filter) RemoveAllowedUserAgent

func (f *Filter) RemoveAllowedUserAgent(userAgent string) error

RemoveAllowedUserAgent dynamically removes a User-Agent string from the allowed list. This method is thread-safe and takes effect immediately.

func (*Filter) RemoveBlockedIP

func (f *Filter) RemoveBlockedIP(ip string) error

RemoveBlockedIP dynamically removes an IP address or CIDR range from the blocked list. This method is thread-safe and takes effect immediately.

func (*Filter) RemoveBlockedUserAgent

func (f *Filter) RemoveBlockedUserAgent(userAgent string) error

RemoveBlockedUserAgent dynamically removes a User-Agent string from the blocked list. This method is thread-safe and takes effect immediately.

type FilterBlockReason

type FilterBlockReason struct {
	Type  AuditEventType // The audit event type for this block
	Value string         // The specific value that caused the block
	Rule  string         // The rule that caused the block
}

FilterBlockReason represents the reason why a request was blocked

type FilterConfig

type FilterConfig struct {
	// AllowedIPs is a list of IP addresses or CIDR ranges that are allowed.
	// Only requests from these IPs will be allowed. All other IPs are blocked.
	// Set via WithAllowedIPs().
	//
	// IP formats supported:
	//   - Single IP: "192.168.1.100"
	//   - CIDR range: "10.0.0.0/8", "192.168.1.0/24"
	//   - IPv6: "2001:db8::1", "2001:db8::/32"
	//
	// Use cases:
	//   - Restrict admin interfaces to office IPs
	//   - Allow only partner/client IPs
	//   - Internal-only APIs
	//   - Development/staging environment protection
	//
	// If empty, all IPs are allowed unless blocked by BlockedIPs.
	AllowedIPs []string

	// BlockedIPs is a list of IP addresses or CIDR ranges that are blocked.
	// Requests from these IPs will be denied with the configured status code.
	// Set via WithBlockedIPs().
	//
	// IP formats supported:
	//   - Single IP: "192.168.1.100"
	//   - CIDR range: "10.0.0.0/8", "192.168.1.0/24"
	//   - IPv6: "2001:db8::1", "2001:db8::/32"
	//
	// Use cases:
	//   - Block known malicious IPs
	//   - Prevent competitor scraping
	//   - Geographic restrictions
	//   - Temporary IP bans
	//
	// Note: BlockedIPs takes precedence over AllowedIPs.
	// If an IP is in both lists, it will be blocked.
	BlockedIPs []string

	// AllowedUserAgents is a list of exact User-Agent strings that are allowed.
	// Only requests with these exact User-Agent headers will be allowed.
	// Set via WithAllowedUserAgents().
	//
	// For pattern matching instead of exact strings, use AllowedUserAgentsRegex.
	//
	// Use cases:
	//   - Restrict API to your apps only
	//   - Block automated scrapers
	//   - Allow only supported browsers
	//   - Partner API access control
	//
	// If empty, all User-Agents are allowed unless blocked by BlockedUserAgents.
	AllowedUserAgents []string

	// AllowedUserAgentsRegex is a list of regex patterns for allowed User-Agents.
	// Only requests with User-Agent headers matching these patterns will be allowed.
	// Set via WithAllowedUserAgentsRegex().
	//
	// Regex features:
	//   - Use standard Go regex syntax
	//   - Case-sensitive matching
	//   - ^ and $ for exact matching
	//   - \d+ for version numbers
	//   - | for alternatives
	//
	// This is more flexible than AllowedUserAgents for version-aware filtering.
	//
	// Examples:
	//   - `Chrome/\d+\.\d+` - Any Chrome browser
	//   - `^MyApp/\d+\.\d+ \((iOS|Android)\)$` - Your app with any version
	AllowedUserAgentsRegex []string

	// BlockedUserAgents is a list of exact User-Agent strings that are blocked.
	// Requests with these exact User-Agent headers will be denied.
	// Set via WithBlockedUserAgents().
	//
	// For pattern matching instead of exact strings, use BlockedUserAgentsRegex.
	//
	// Use cases:
	//   - Block automated scrapers
	//   - Prevent bot traffic
	//   - Block specific tools
	//   - Temporary user-agent bans
	//
	// Note: BlockedUserAgents takes precedence over AllowedUserAgents.
	BlockedUserAgents []string

	// BlockedUserAgentsRegex is a list of regex patterns for blocked User-Agents.
	// Requests with User-Agent headers matching these patterns will be denied.
	// Set via WithBlockedUserAgentsRegex().
	//
	// Regex features:
	//   - (?i) for case-insensitive matching
	//   - Use standard Go regex syntax
	//   - ^ and $ for exact matching
	//   - | for alternatives
	//
	// Examples:
	//   - `(?i)(bot|crawler|spider|scraper)` - Block all bots and crawlers
	//   - `^(curl|wget|python-requests)` - Block command line tools
	//
	// Note: BlockedUserAgentsRegex takes precedence over AllowedUserAgentsRegex.
	BlockedUserAgentsRegex []string

	// AllowedHeaders is a map of header names to exact allowed values.
	// Only requests with headers matching the specified exact values will be allowed.
	// Set via WithAllowedHeaders().
	//
	// Header matching:
	//   - Header names are case-insensitive
	//   - Values must match exactly (case-sensitive)
	//   - Multiple allowed values per header
	//   - All specified headers must be present
	//
	// Use cases:
	//   - API version enforcement
	//   - Content-Type validation
	//   - Custom authentication schemes
	//   - Partner-specific headers
	//
	// For pattern matching instead of exact values, use AllowedHeadersRegex.
	AllowedHeaders map[string][]string

	// AllowedHeadersRegex is a map of header names to regex patterns for allowed values.
	// Only requests with headers matching the specified patterns will be allowed.
	// Set via WithAllowedHeadersRegex().
	//
	// Regex features:
	//   - Header names are case-insensitive
	//   - Use standard Go regex syntax
	//   - ^ and $ for exact matching
	//   - Multiple patterns per header (OR logic)
	//
	// Examples:
	//   - "Authorization": [`^Bearer [A-Za-z0-9+/=]+$`] - Any Bearer token
	//   - "X-API-Version": [`^v\d+\.\d+$`] - Semantic versioning
	//
	// This is more flexible than AllowedHeaders for pattern-based validation.
	AllowedHeadersRegex map[string][]string

	// BlockedHeaders is a map of header names to exact blocked values.
	// Requests with headers matching the specified exact values will be denied.
	// Set via WithBlockedHeaders().
	//
	// Header matching:
	//   - Header names are case-insensitive
	//   - Values must match exactly (case-sensitive)
	//   - Multiple blocked values per header
	//   - Any matching header causes blocking
	//
	// Use cases:
	//   - Block deprecated API versions
	//   - Security header filtering
	//   - Malicious request detection
	//   - Legacy client blocking
	//
	// Note: BlockedHeaders takes precedence over AllowedHeaders.
	BlockedHeaders map[string][]string

	// BlockedHeadersRegex is a map of header names to regex patterns for blocked values.
	// Requests with headers matching the specified patterns will be denied.
	// Set via WithBlockedHeadersRegex().
	//
	// Regex features:
	//   - Header names are case-insensitive
	//   - (?i) for case-insensitive pattern matching
	//   - Use standard Go regex syntax
	//   - Multiple patterns per header (OR logic)
	//
	// Examples:
	//   - "X-Forwarded-For": [`(10\.0\.0\.|192\.168\.)`] - Block internal IPs
	//   - "User-Agent": [`(?i)(bot|crawler|spider)`] - Block bots
	//
	// Note: BlockedHeadersRegex takes precedence over AllowedHeadersRegex.
	BlockedHeadersRegex map[string][]string

	// AllowedQueryParams is a map of query parameter names to exact allowed values.
	// Only requests with query parameters matching the specified exact values will be allowed.
	// Set via WithAllowedQueryParams().
	//
	// Parameter matching:
	//   - Parameter names are case-sensitive
	//   - Values must match exactly (case-sensitive)
	//   - Multiple allowed values per parameter
	//   - All specified parameters must be present
	//
	// Use cases:
	//   - API parameter validation
	//   - Prevent SQL injection via query params
	//   - Business logic validation
	//   - Feature flag enforcement
	//
	// For pattern matching instead of exact values, use AllowedQueryParamsRegex.
	AllowedQueryParams map[string][]string

	// AllowedQueryParamsRegex is a map of query parameter names to regex patterns for allowed values.
	// Only requests with query parameters matching the specified patterns will be allowed.
	// Set via WithAllowedQueryParamsRegex().
	//
	// Regex features:
	//   - Parameter names are case-sensitive
	//   - Use standard Go regex syntax
	//   - ^ and $ for exact matching
	//   - Multiple patterns per parameter (OR logic)
	//
	// Examples:
	//   - "id": [`^\d+$`] - Numeric IDs only
	//   - "email": [`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`] - Email format
	//   - "uuid": [`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`] - UUID format
	//
	// This is more flexible than AllowedQueryParams for format validation.
	AllowedQueryParamsRegex map[string][]string

	// BlockedQueryParams is a map of query parameter names to exact blocked values.
	// Requests with query parameters matching the specified exact values will be denied.
	// Set via WithBlockedQueryParams().
	//
	// Parameter matching:
	//   - Parameter names are case-sensitive
	//   - Values must match exactly (case-sensitive)
	//   - Multiple blocked values per parameter
	//   - Any matching parameter causes blocking
	//
	// Use cases:
	//   - Security parameter filtering
	//   - Debug mode blocking in production
	//   - Malicious query detection
	//   - Legacy parameter deprecation
	//
	// Note: BlockedQueryParams takes precedence over AllowedQueryParams.
	BlockedQueryParams map[string][]string

	// BlockedQueryParamsRegex is a map of query parameter names to regex patterns for blocked values.
	// Requests with query parameters matching the specified patterns will be denied.
	// Set via WithBlockedQueryParamsRegex().
	//
	// Regex features:
	//   - Parameter names are case-sensitive
	//   - (?i) for case-insensitive pattern matching
	//   - Use standard Go regex syntax
	//   - Multiple patterns per parameter (OR logic)
	//
	// Examples:
	//   - "search": [`(?i)(union|select|drop|delete|insert|update)`] - Block SQL injection
	//   - "callback": [`(?i)(<script|javascript:|vbscript:)`] - Block script injection
	//   - "query": [`.{1000,}`] - Block excessive length
	//
	// Note: BlockedQueryParamsRegex takes precedence over AllowedQueryParamsRegex.
	BlockedQueryParamsRegex map[string][]string

	// ExcludePaths are paths that should be excluded from request filtering.
	// Requests to these paths will bypass all filtering rules.
	// Set via WithFilterExcludePaths().
	//
	// Common exclusions:
	//   - Health checks: "/health", "/ping"
	//   - Public APIs: "/public/*", "/api/public/*"
	//   - Documentation: "/docs/*", "/swagger/*"
	//   - Static assets: "/static/*", "/assets/*"
	//   - Monitoring: "/metrics", "/status"
	//
	// Path matching supports wildcards (*) for pattern matching.
	// Excluded paths bypass ALL filtering rules (IP, User-Agent, headers, query params).
	ExcludePaths []string

	// IncludePaths are paths that should be included in request filtering.
	// If set, only requests to these paths will be subject to filtering rules.
	// Set via WithFilterIncludePaths().
	//
	// If both IncludePaths and ExcludePaths are set:
	//   1. Paths must match IncludePaths to be filtered
	//   2. Paths in ExcludePaths are then excluded from filtering
	//
	// Use cases:
	//   - Protect only sensitive endpoints
	//   - Apply filtering to specific API versions
	//   - Filter only external-facing endpoints
	//   - Granular security control
	//
	// Path matching supports wildcards (*) for pattern matching.
	IncludePaths []string

	// StatusCode is the HTTP status code returned when requests are blocked by filters.
	// Set via WithFilterStatusCode(). Default is 403 (Forbidden) if not set.
	//
	// Common status codes:
	//   - 403 Forbidden (recommended) - Clear about blocking
	//   - 404 Not Found - Hides endpoint existence
	//   - 401 Unauthorized - Suggests authentication needed
	//   - 429 Too Many Requests - Can mislead attackers
	//
	// Choose based on your security strategy and user experience needs.
	StatusCode int

	// Message is the response body returned when requests are blocked by filters.
	// Set via WithFilterMessage(). Default is "Request blocked by security filter" if not set.
	//
	// Best practices:
	//   - Be clear but not too specific about the filter
	//   - Include contact information for legitimate users
	//   - Avoid revealing security implementation details
	//   - Keep messages user-friendly
	//
	// The message is returned as plain text in the response body.
	Message string

	// TrustedProxies is a list of trusted proxy IP addresses or CIDR ranges
	// for accurate client IP detection in filtering.
	// Set via WithFilterTrustedProxies().
	//
	// How it works:
	//   - Without trusted proxies: Uses r.RemoteAddr (proxy IP) for IP filtering
	//   - With trusted proxies: Uses X-Forwarded-For or X-Real-IP headers
	//
	// Common proxy ranges:
	//   - AWS ALB: Check AWS documentation for current ranges
	//   - Cloudflare: Use Cloudflare's published IP ranges
	//   - Internal load balancers: Your internal network ranges
	//   - Docker networks: 172.16.0.0/12, 10.0.0.0/8
	//
	// Security considerations:
	//   - Only list IPs you actually trust
	//   - Malicious clients can spoof X-Forwarded-For headers
	//   - Ensure proxy properly validates and forwards real client IPs
	TrustedProxies []string
}

FilterConfig holds configuration for request filtering middleware. This enables filtering requests based on IP addresses, User-Agents, headers, and query parameters.

Request filtering helps protect your server from:

  • Malicious IP addresses
  • Bot and scraper traffic
  • Invalid or dangerous requests
  • Geographic restrictions
  • Content-based attacks

Example configuration:

filter := FilterConfig{
	AllowedIPs: []string{"192.168.1.0/24", "10.0.0.0/8"},
	BlockedUserAgents: []string{"BadBot", "Scraper"},
	AllowedHeaders: map[string][]string{
		"X-API-Version": {"v1", "v2"},
	},
	StatusCode: 403,
	Message: "Access denied by security filter",
}

This is a pure data structure without any logic - the filtering logic is implemented in the middleware that uses this configuration.

type FilterConfiguration

type FilterConfiguration struct {
	AllowedIPs              []string            `yaml:"allowed_ips" json:"allowed_ips" env:"SERVEX_FILTER_ALLOWED_IPS"`
	BlockedIPs              []string            `yaml:"blocked_ips" json:"blocked_ips" env:"SERVEX_FILTER_BLOCKED_IPS"`
	AllowedUserAgents       []string            `yaml:"allowed_user_agents" json:"allowed_user_agents" env:"SERVEX_FILTER_ALLOWED_USER_AGENTS"`
	AllowedUserAgentsRegex  []string            `yaml:"allowed_user_agents_regex" json:"allowed_user_agents_regex" env:"SERVEX_FILTER_ALLOWED_USER_AGENTS_REGEX"`
	BlockedUserAgents       []string            `yaml:"blocked_user_agents" json:"blocked_user_agents" env:"SERVEX_FILTER_BLOCKED_USER_AGENTS"`
	BlockedUserAgentsRegex  []string            `yaml:"blocked_user_agents_regex" json:"blocked_user_agents_regex" env:"SERVEX_FILTER_BLOCKED_USER_AGENTS_REGEX"`
	AllowedHeaders          map[string][]string `yaml:"allowed_headers" json:"allowed_headers"`
	AllowedHeadersRegex     map[string][]string `yaml:"allowed_headers_regex" json:"allowed_headers_regex"`
	BlockedHeaders          map[string][]string `yaml:"blocked_headers" json:"blocked_headers"`
	BlockedHeadersRegex     map[string][]string `yaml:"blocked_headers_regex" json:"blocked_headers_regex"`
	AllowedQueryParams      map[string][]string `yaml:"allowed_query_params" json:"allowed_query_params"`
	AllowedQueryParamsRegex map[string][]string `yaml:"allowed_query_params_regex" json:"allowed_query_params_regex"`
	BlockedQueryParams      map[string][]string `yaml:"blocked_query_params" json:"blocked_query_params"`
	BlockedQueryParamsRegex map[string][]string `yaml:"blocked_query_params_regex" json:"blocked_query_params_regex"`
	ExcludePaths            []string            `yaml:"exclude_paths" json:"exclude_paths" env:"SERVEX_FILTER_EXCLUDE_PATHS"`
	IncludePaths            []string            `yaml:"include_paths" json:"include_paths" env:"SERVEX_FILTER_INCLUDE_PATHS"`
	StatusCode              int                 `yaml:"status_code" json:"status_code" env:"SERVEX_FILTER_STATUS_CODE"`
	Message                 string              `yaml:"message" json:"message" env:"SERVEX_FILTER_MESSAGE"`
	TrustedProxies          []string            `yaml:"trusted_proxies" json:"trusted_proxies" env:"SERVEX_FILTER_TRUSTED_PROXIES"`
}

FilterConfiguration represents request filtering configuration

type HTTPSRedirectConfig added in v2.1.0

type HTTPSRedirectConfig struct {
	// Enabled determines whether HTTP to HTTPS redirection is active.
	// Must be set to true for automatic HTTPS redirection to work.
	// Set via WithHTTPSRedirect() or WithHTTPSRedirectPermanent().
	Enabled bool

	// Permanent determines the type of redirect to use.
	// If true, uses HTTP 301 (permanent redirect) for better SEO.
	// If false, uses HTTP 302 (temporary redirect) for testing/development.
	// Set via WithHTTPSRedirect() or WithHTTPSRedirectPermanent().
	//
	// Recommended values:
	//   - Production: true (301 permanent redirect)
	//   - Development/Testing: false (302 temporary redirect)
	//
	// Default: true (permanent redirect)
	Permanent bool

	// TrustedProxies is a list of trusted proxy IP addresses or CIDR ranges
	// for accurate HTTP/HTTPS detection when behind load balancers or proxies.
	// Set via WithHTTPSRedirectTrustedProxies().
	//
	// When behind proxies, the server cannot detect HTTPS from r.TLS alone.
	// This setting allows checking X-Forwarded-Proto and similar headers
	// only when the request comes from trusted proxy IPs.
	//
	// Common proxy ranges:
	//   - AWS ALB: Check AWS documentation for current ranges
	//   - Cloudflare: Use Cloudflare's published IP ranges
	//   - Internal load balancers: Your internal network ranges
	//   - Docker networks: 172.16.0.0/12, 10.0.0.0/8
	//
	// Security note: Only list IPs you actually trust. Malicious clients
	// can spoof X-Forwarded-Proto headers if the proxy IP is trusted.
	TrustedProxies []string

	// ExcludePaths are paths that should be excluded from HTTPS redirection.
	// Requests to these paths will not be redirected to HTTPS.
	// Set via WithHTTPSRedirectExcludePaths().
	//
	// Common exclusions:
	//   - Health checks: "/health", "/ping" (for load balancers that use HTTP)
	//   - Let's Encrypt challenges: "/.well-known/acme-challenge/*"
	//   - Development endpoints: "/debug/*"
	//   - Legacy integrations that require HTTP: "/legacy/*"
	//
	// Path matching supports wildcards (*) for pattern matching.
	// Use sparingly - most paths should use HTTPS for security.
	ExcludePaths []string

	// IncludePaths are paths that should be included in HTTPS redirection.
	// If set, only requests to these paths will be redirected to HTTPS.
	// Set via WithHTTPSRedirectIncludePaths().
	//
	// If both IncludePaths and ExcludePaths are set:
	//   1. Paths must match IncludePaths to be considered for redirection
	//   2. Paths in ExcludePaths are then excluded from redirection
	//
	// Use cases:
	//   - Gradual HTTPS migration: Start with specific paths
	//   - Mixed HTTP/HTTPS applications: Only secure sensitive areas
	//   - Testing HTTPS setup: Limit scope during testing
	//
	// Path matching supports wildcards (*) for pattern matching.
	// Leave empty to redirect all paths (recommended for production).
	IncludePaths []string
}

HTTPSRedirectConfig holds configuration for automatic HTTP to HTTPS redirection. This enables server-level enforcement of HTTPS connections by automatically redirecting all HTTP requests to their HTTPS equivalent.

Security benefits:

  • Enforces encrypted connections
  • Prevents accidental plain-text transmission
  • Improves SEO rankings (search engines prefer HTTPS)
  • Required for modern web features (Service Workers, Geolocation, etc.)

Example configuration:

httpsRedirect := HTTPSRedirectConfig{
	Enabled: true,
	Permanent: true,
	TrustedProxies: []string{"10.0.0.0/8", "172.16.0.0/12"},
	ExcludePaths: []string{"/health", "/.well-known/*"},
}

type HTTPSRedirectConfiguration added in v2.2.0

type HTTPSRedirectConfiguration struct {
	Enabled        bool     `yaml:"enabled" json:"enabled" env:"SERVEX_SERVER_HTTPS_REDIRECT_ENABLED"`
	Permanent      bool     `yaml:"permanent" json:"permanent" env:"SERVEX_SERVER_HTTPS_REDIRECT_PERMANENT"`
	TrustedProxies []string `yaml:"trusted_proxies" json:"trusted_proxies" env:"SERVEX_SERVER_HTTPS_REDIRECT_TRUSTED_PROXIES"`
	ExcludePaths   []string `yaml:"exclude_paths" json:"exclude_paths" env:"SERVEX_SERVER_HTTPS_REDIRECT_EXCLUDE_PATHS"`
	IncludePaths   []string `yaml:"include_paths" json:"include_paths" env:"SERVEX_SERVER_HTTPS_REDIRECT_INCLUDE_PATHS"`
}

HTTPSRedirectConfiguration represents HTTPS redirection configuration

type HealthCheckConfig

type HealthCheckConfig struct {
	// Enabled indicates if health checking is enabled
	Enabled bool `yaml:"enabled" json:"enabled"`
	// DefaultInterval for health checks
	DefaultInterval time.Duration `yaml:"default_interval" json:"default_interval"`
	// Timeout for health check requests
	Timeout time.Duration `yaml:"timeout" json:"timeout"`
	// RetryCount before marking backend as unhealthy
	RetryCount int `yaml:"retry_count" json:"retry_count"`
}

HealthCheckConfig configures health checking

type InitialUser

type InitialUser struct {
	// Username is the unique username for the user.
	// This will be used for login and user identification.
	//
	// Requirements:
	//   - Must be unique across all users
	//   - Should follow your application's username policy
	//   - Cannot be empty
	//
	// Examples: "admin", "testuser", "service-account"
	Username string

	// Password is the plain text password for the user.
	// The password will be automatically hashed before storing in the database.
	//
	// Security considerations:
	//   - Use strong passwords (consider password generators)
	//   - Minimum 8 characters recommended
	//   - Include mix of letters, numbers, and symbols
	//   - Never commit passwords to source control
	//   - Consider loading from environment variables
	//
	// Example: os.Getenv("ADMIN_PASSWORD") or "SecurePassword123!"
	Password string

	// Roles are the roles assigned to the user upon creation.
	// These roles determine the user's permissions and access levels.
	//
	// Common roles:
	//   - "admin": Full system access
	//   - "user": Standard user access
	//   - "moderator": Content management access
	//   - "api": API-only access
	//
	// Users can have multiple roles for fine-grained permissions.
	// Additional roles can be assigned later through user management.
	Roles []UserRole
}

InitialUser represents a user to be created during server startup. This is used with AuthConfig.InitialUsers to seed the database with admin accounts or test users. Set via WithAuthInitialUsers().

Example usage:

initialUsers := []InitialUser{
	{
		Username: "admin",
		Password: "secure-admin-password",
		Roles:    []UserRole{"admin", "user"},
	},
	{
		Username: "testuser",
		Password: "test-password",
		Roles:    []UserRole{"user"},
	},
}

Security considerations:

  • Use strong, unique passwords
  • Consider loading passwords from environment variables
  • Remove or change default passwords in production
  • Limit to essential accounts only

type LoadBalancingStrategy

type LoadBalancingStrategy string

LoadBalancingStrategy defines the load balancing algorithm

const (
	// RoundRobinStrategy cycles through backends in order
	RoundRobinStrategy LoadBalancingStrategy = "round_robin"
	// WeightedRoundRobinStrategy cycles through backends based on weights
	WeightedRoundRobinStrategy LoadBalancingStrategy = "weighted_round_robin"
	// LeastConnectionsStrategy routes to backend with fewest active connections
	LeastConnectionsStrategy LoadBalancingStrategy = "least_connections"
	// RandomStrategy routes to a random backend
	RandomStrategy LoadBalancingStrategy = "random"
	// WeightedRandomStrategy routes to a random backend based on weights
	WeightedRandomStrategy LoadBalancingStrategy = "weighted_random"
	// IPHashStrategy routes based on client IP hash (session affinity)
	IPHashStrategy LoadBalancingStrategy = "ip_hash"
)

type LocationFilterConfig

type LocationFilterConfig struct {
	// PathPatterns are the URL path patterns this config applies to.
	// Supports wildcards using filepath.Match syntax (e.g., "/api/*", "/admin/*").
	// If multiple patterns are provided, any match will apply this config.
	//
	// Examples:
	//   - ["/api/*"] - All API endpoints
	//   - ["/admin/*", "/dashboard/*"] - Admin and dashboard areas
	//   - ["/auth/login", "/auth/register"] - Specific auth endpoints
	//   - ["/upload/*"] - File upload endpoints
	PathPatterns []string

	// Config is the filter configuration to apply for matching paths.
	// This contains all the filtering settings like allowed/blocked IPs,
	// user agents, headers, query parameters, etc.
	Config FilterConfig
}

LocationFilterConfig defines a filter configuration for specific locations. This allows different filter rules to be applied to different URL paths.

type LocationRateLimitConfig

type LocationRateLimitConfig struct {
	// PathPatterns are the URL path patterns this config applies to.
	// Supports wildcards using filepath.Match syntax (e.g., "/api/*", "/admin/*").
	// If multiple patterns are provided, any match will apply this config.
	//
	// Examples:
	//   - ["/api/*"] - All API endpoints
	//   - ["/admin/*", "/dashboard/*"] - Admin and dashboard areas
	//   - ["/auth/login", "/auth/register"] - Specific auth endpoints
	//   - ["/upload/*"] - File upload endpoints
	PathPatterns []string

	// Config is the rate limit configuration to apply for matching paths.
	// This contains all the rate limiting settings like requests per interval,
	// burst size, status codes, etc.
	Config RateLimitConfig
}

LocationRateLimitConfig defines a rate limit configuration for specific locations. This allows different rate limits to be applied to different URL paths.

type Logger

type Logger interface {
	// Debug is using to log successful requests.
	Debug(msg string, fields ...any)
	// Info is using to log 'http(s) server started'
	Info(msg string, fields ...any)
	// Error is using to log request errors, panics, serve errors and shutodwn in StartContext errors
	Error(msg string, fields ...any)
}

Logger is an interface for logger to log messages.

type LoggingConfiguration

type LoggingConfiguration struct {
	DisableRequestLogging bool     `yaml:"disable_request_logging" json:"disable_request_logging" env:"SERVEX_LOGGING_DISABLE_REQUEST_LOGGING"`
	NoLogClientErrors     bool     `yaml:"no_log_client_errors" json:"no_log_client_errors" env:"SERVEX_LOGGING_NO_LOG_CLIENT_ERRORS"`
	LogFields             []string `yaml:"log_fields" json:"log_fields" env:"SERVEX_LOGGING_LOG_FIELDS"`
}

LoggingConfiguration represents logging configuration

type MemoryAuthDatabase

type MemoryAuthDatabase struct {
	// contains filtered or unexported fields
}

MockAuthDatabase provides a mock implementation of the AuthDatabase interface for testing.

func NewMemoryAuthDatabase

func NewMemoryAuthDatabase() *MemoryAuthDatabase

func (*MemoryAuthDatabase) FindAll

func (db *MemoryAuthDatabase) FindAll(ctx context.Context) ([]User, error)

func (*MemoryAuthDatabase) FindByID

func (db *MemoryAuthDatabase) FindByID(ctx context.Context, id string) (User, bool, error)

func (*MemoryAuthDatabase) FindByUsername

func (db *MemoryAuthDatabase) FindByUsername(ctx context.Context, username string) (User, bool, error)

func (*MemoryAuthDatabase) NewUser

func (db *MemoryAuthDatabase) NewUser(ctx context.Context, username string, passwordHash string, roles ...UserRole) (string, error)

func (*MemoryAuthDatabase) UpdateUser

func (db *MemoryAuthDatabase) UpdateUser(ctx context.Context, id string, diff *UserDiff) error

type Metrics

type Metrics interface {
	// HandleRequest is called on each request to collect metrics.
	HandleRequest(r *http.Request)

	// HandleResponse is called on each response to collect metrics.
	HandleResponse(r *http.Request, w http.ResponseWriter, statusCode int, duration time.Duration)
}

Metrics is an interface for collecting metrics on each request. [Metrics.HandleRequest] is called on each request. [Metrics.HandleResponse] is called on each response.

type MiddlewareRouter

type MiddlewareRouter interface {
	// Use adds one or more middleware functions to the router.
	// Middleware functions are executed in the order they are added.
	Use(middleware ...mux.MiddlewareFunc)
}

MiddlewareRouter represents a router that supports adding middleware functions. This interface is typically implemented by router packages like gorilla/mux and allows servex to register its middleware functions with different router implementations.

The middleware functions registered through this interface provide essential features like logging, security headers, rate limiting, authentication, and more.

type NoopAuditLogger

type NoopAuditLogger struct{}

NoopAuditLogger is a no-op implementation of AuditLogger

func (*NoopAuditLogger) LogAuthenticationEvent

func (nal *NoopAuditLogger) LogAuthenticationEvent(eventType AuditEventType, r *http.Request, userID string, success bool, details map[string]any)

func (*NoopAuditLogger) LogCSRFEvent

func (nal *NoopAuditLogger) LogCSRFEvent(eventType AuditEventType, r *http.Request, details map[string]any)

func (*NoopAuditLogger) LogFilterEvent

func (nal *NoopAuditLogger) LogFilterEvent(eventType AuditEventType, r *http.Request, filterType, filterValue, rule string)

func (*NoopAuditLogger) LogRateLimitEvent

func (nal *NoopAuditLogger) LogRateLimitEvent(r *http.Request, key string, details map[string]any)

func (*NoopAuditLogger) LogSecurityEvent

func (nal *NoopAuditLogger) LogSecurityEvent(event AuditEvent)

func (*NoopAuditLogger) LogSuspiciousActivity

func (nal *NoopAuditLogger) LogSuspiciousActivity(r *http.Request, activityType string, details map[string]any)

type Option

type Option func(*Options)

func APIServerPreset

func APIServerPreset() []Option

APIServerPreset returns options for a typical REST API server. Features: JWT auth support, API rate limiting, security headers, CORS-friendly, request size limits, compression.

func DevelopmentPreset

func DevelopmentPreset() []Option

DevelopmentPreset returns options suitable for development environment. Features: basic logging, no security restrictions, no rate limiting, detailed error reporting.

func HighSecurityPreset

func HighSecurityPreset(cert tls.Certificate) []Option

HighSecurityPreset returns options for high-security applications. Features: strict security headers, CSRF protection, request filtering, comprehensive rate limiting, audit logging.

func MergePresets added in v2.2.0

func MergePresets(presets ...[]Option) []Option

MergePresets merges multiple presets into a single slice of options.

func MergeWithPreset added in v2.2.0

func MergeWithPreset(preset []Option, opts ...Option) []Option

MergeWithPreset merges a preset with additional options.

func MicroservicePreset

func MicroservicePreset() []Option

MicroservicePreset returns options for microservice environments. Features: minimal security (behind gateway), fast timeouts, health checks, size limits.

func ProductionPreset

func ProductionPreset(cert tls.Certificate) []Option

ProductionPreset returns options suitable for production environment. Features: security headers, CSRF protection, rate limiting, request logging, health endpoints, metrics, compression.

func TLSPreset added in v2.2.0

func TLSPreset(certFile, keyFile string, cert ...tls.Certificate) []Option

TLSPreset returns options for quick SSL/TLS setup. Provide cert object or cert and key files.

func WebAppPreset

func WebAppPreset(cert tls.Certificate) []Option

WebAppPreset returns options for serving web applications. Features: web security headers, CSRF protection, content protection, static file friendly, size limits, compression.

func WithAllowedHeaders

func WithAllowedHeaders(headers map[string][]string) Option

WithAllowedHeaders restricts requests based on header values. Only requests with headers matching the specified exact values will be allowed.

Example:

// Require specific API version
server := servex.New(servex.WithAllowedHeaders(map[string][]string{
	"X-API-Version": {"v1", "v2"},
	"Content-Type":  {"application/json"},
}))

// Require authentication header
server := servex.New(servex.WithAllowedHeaders(map[string][]string{
	"Authorization": {"Bearer token1", "Bearer token2"},
}))

Header matching:

  • Header names are case-insensitive
  • Values must match exactly (case-sensitive)
  • Multiple allowed values per header
  • All specified headers must be present

Use cases:

  • API version enforcement
  • Content-Type validation
  • Custom authentication schemes
  • Partner-specific headers

For pattern matching instead of exact values, use WithAllowedHeadersRegex().

func WithAllowedHeadersRegex

func WithAllowedHeadersRegex(headers map[string][]string) Option

WithAllowedHeadersRegex restricts requests based on header regex patterns. Only requests with headers matching the specified patterns will be allowed.

Example:

// Allow any Bearer token
server := servex.New(servex.WithAllowedHeadersRegex(map[string][]string{
	"Authorization": {`^Bearer [A-Za-z0-9+/=]+$`},
}))

// Allow semantic versioning
server := servex.New(servex.WithAllowedHeadersRegex(map[string][]string{
	"X-API-Version": {`^v\d+\.\d+$`},  // v1.0, v2.1, etc.
}))

// Validate custom headers
server := servex.New(servex.WithAllowedHeadersRegex(map[string][]string{
	"X-Request-ID": {`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`},
}))

Regex features:

  • Header names are case-insensitive
  • Use standard Go regex syntax
  • ^ and $ for exact matching
  • Multiple patterns per header (OR logic)

This is more flexible than WithAllowedHeaders() for pattern-based validation.

func WithAllowedIPs

func WithAllowedIPs(ips ...string) Option

WithAllowedIPs restricts access to specific IP addresses or CIDR ranges. Only requests from these IPs will be allowed. All other IPs are blocked.

Example:

// Allow specific office IPs
server := servex.New(servex.WithAllowedIPs(
	"192.168.1.0/24",    // Office network
	"203.0.113.100",     // VPN gateway
	"10.0.0.0/8",        // Internal network
))

// Allow only localhost
server := servex.New(servex.WithAllowedIPs("127.0.0.1", "::1"))

IP formats supported:

  • Single IP: "192.168.1.100"
  • CIDR range: "10.0.0.0/8", "192.168.1.0/24"
  • IPv6: "2001:db8::1", "2001:db8::/32"

Use cases:

  • Restrict admin interfaces to office IPs
  • Allow only partner/client IPs
  • Internal-only APIs
  • Development/staging environment protection

If empty, all IPs are allowed unless blocked by WithBlockedIPs().

func WithAllowedQueryParams

func WithAllowedQueryParams(params map[string][]string) Option

WithAllowedQueryParams restricts requests based on query parameter values. Only requests with query parameters matching the specified exact values will be allowed.

Example:

// Require specific API version
server := servex.New(servex.WithAllowedQueryParams(map[string][]string{
	"version": {"v1", "v2"},
	"format":  {"json", "xml"},
}))

// Require valid sort parameters
server := servex.New(servex.WithAllowedQueryParams(map[string][]string{
	"sort": {"name", "date", "price"},
	"order": {"asc", "desc"},
}))

Parameter matching:

  • Parameter names are case-sensitive
  • Values must match exactly (case-sensitive)
  • Multiple allowed values per parameter
  • All specified parameters must be present

Use cases:

  • API parameter validation
  • Prevent SQL injection via query params
  • Business logic validation
  • Feature flag enforcement

For pattern matching instead of exact values, use WithAllowedQueryParamsRegex().

func WithAllowedQueryParamsRegex

func WithAllowedQueryParamsRegex(params map[string][]string) Option

WithAllowedQueryParamsRegex restricts requests based on query parameter regex patterns. Only requests with query parameters matching the specified patterns will be allowed.

Example:

// Allow numeric IDs only
server := servex.New(servex.WithAllowedQueryParamsRegex(map[string][]string{
	"id": {`^\d+$`},
	"page": {`^[1-9]\d*$`},  // Positive integers only
}))

// Validate email format
server := servex.New(servex.WithAllowedQueryParamsRegex(map[string][]string{
	"email": {`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`},
}))

// Allow UUID format
server := servex.New(servex.WithAllowedQueryParamsRegex(map[string][]string{
	"uuid": {`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`},
}))

Regex features:

  • Parameter names are case-sensitive
  • Use standard Go regex syntax
  • ^ and $ for exact matching
  • Multiple patterns per parameter (OR logic)

This is more flexible than WithAllowedQueryParams() for format validation.

func WithAllowedUserAgents

func WithAllowedUserAgents(userAgents ...string) Option

WithAllowedUserAgents restricts access to specific User-Agent strings. Only requests with these exact User-Agent headers will be allowed.

Example:

// Allow only your mobile app
server := servex.New(servex.WithAllowedUserAgents(
	"MyApp/1.0 (iOS)",
	"MyApp/1.0 (Android)",
))

// Allow specific browsers
server := servex.New(servex.WithAllowedUserAgents(
	"Mozilla/5.0 Chrome/120.0.0.0",
	"Mozilla/5.0 Safari/537.36",
))

For pattern matching instead of exact strings, use WithAllowedUserAgentsRegex().

Use cases:

  • Restrict API to your apps only
  • Block automated scrapers
  • Allow only supported browsers
  • Partner API access control

If empty, all User-Agents are allowed unless blocked by WithBlockedUserAgents().

func WithAllowedUserAgentsRegex

func WithAllowedUserAgentsRegex(patterns ...string) Option

WithAllowedUserAgentsRegex restricts access using User-Agent regex patterns. Only requests with User-Agent headers matching these patterns will be allowed.

Example:

// Allow any Chrome browser
server := servex.New(servex.WithAllowedUserAgentsRegex(
	`Chrome/\d+\.\d+`,
))

// Allow your app with any version
server := servex.New(servex.WithAllowedUserAgentsRegex(
	`^MyApp/\d+\.\d+ \((iOS|Android)\)$`,
))

// Allow major browsers
server := servex.New(servex.WithAllowedUserAgentsRegex(
	`(Chrome|Firefox|Safari|Edge)/\d+`,
))

Regex features:

  • Use standard Go regex syntax
  • Case-sensitive matching
  • ^ and $ for exact matching
  • \d+ for version numbers
  • | for alternatives

This is more flexible than WithAllowedUserAgents() for version-aware filtering.

func WithAuditLogHeaders

func WithAuditLogHeaders(include bool) Option

WithAuditLogHeaders configures whether to include HTTP headers in audit logs. This should be used carefully as headers may contain sensitive information.

Example:

// Include headers in audit logs (for detailed security analysis)
server := servex.New(
	servex.WithDefaultAuditLogger(),
	servex.WithAuditLogHeaders(true),
)

When enabled, the audit logger will include non-sensitive headers in security events. Sensitive headers like Authorization, Cookie, X-API-Key are always excluded.

func WithAuditLogger

func WithAuditLogger(logger AuditLogger) Option

WithAuditLogger sets a custom audit logger for security events. The audit logger is used to log authentication events, rate limiting violations, filter blocks, CSRF attacks, and other security-related events.

Example:

// Custom audit logger that sends to external SIEM
type SIEMAuditLogger struct {
	client *siem.Client
}

func (s *SIEMAuditLogger) LogSecurityEvent(event servex.AuditEvent) {
	s.client.SendEvent(event)
}

server := servex.New(
	servex.WithAuditLogger(&SIEMAuditLogger{client: siemClient}),
)

Use for:

  • Integration with Security Information and Event Management (SIEM) systems
  • Custom audit log formatting and routing
  • Compliance with specific regulatory requirements
  • Integration with threat intelligence platforms

func WithAuth

func WithAuth(db AuthDatabase) Option

WithAuth enables JWT-based authentication with a custom database implementation. This activates the full authentication system with user management, roles, and JWT tokens.

The database must implement the AuthDatabase interface for user persistence.

Example:

// Custom database implementation
type MyAuthDB struct {
	users map[string]*User
}

func (db *MyAuthDB) CreateUser(ctx context.Context, user User) error {
	// Implementation
}
// ... implement other AuthDatabase methods

server := servex.New(servex.WithAuth(&MyAuthDB{}))

This automatically registers these endpoints:

  • POST /api/v1/auth/register - User registration
  • POST /api/v1/auth/login - User login
  • POST /api/v1/auth/refresh - Token refresh
  • POST /api/v1/auth/logout - User logout
  • GET /api/v1/auth/me - Current user info

Use this for:

  • Multi-user applications
  • Role-based access control
  • Persistent user data
  • Production authentication systems

func WithAuthBasePath

func WithAuthBasePath(path string) Option

WithAuthBasePath sets the base path for authentication API endpoints. All auth routes will be registered under this path.

Example:

// Custom auth path
server := servex.New(servex.WithAuthBasePath("/auth"))
// Endpoints: /auth/login, /auth/register, etc.

// API versioned path
server := servex.New(servex.WithAuthBasePath("/api/v2/auth"))
// Endpoints: /api/v2/auth/login, /api/v2/auth/register, etc.

Default is "/api/v1/auth" if not set.

Registered endpoints under the base path:

  • POST {basePath}/register
  • POST {basePath}/login
  • POST {basePath}/refresh
  • POST {basePath}/logout
  • GET {basePath}/me

func WithAuthConfig

func WithAuthConfig(auth AuthConfig) Option

WithAuthConfig sets the complete authentication configuration at once. This allows fine-grained control over all authentication settings.

Example:

authConfig := servex.AuthConfig{
	Enabled:                 true,
	Database:                myDB,
	AccessTokenDuration:     15 * time.Minute,
	RefreshTokenDuration:    7 * 24 * time.Hour,
	AuthBasePath:           "/auth",
	IssuerNameInJWT:        "my-app",
	RefreshTokenCookieName: "_refresh",
	RolesOnRegister:        []servex.UserRole{"user"},
	InitialUsers: []servex.InitialUser{
		{Username: "admin", Password: "secure-password", Roles: []servex.UserRole{"admin"}},
	},
}

server := servex.New(servex.WithAuthConfig(authConfig))

Use this when you need to configure multiple authentication settings at once or when loading configuration from files or environment variables.

func WithAuthInitialRoles

func WithAuthInitialRoles(roles ...UserRole) Option

WithAuthInitialRoles sets the default roles assigned to newly registered users. These roles are automatically assigned when users register through the /register endpoint.

Example:

// All new users get "user" role
server := servex.New(servex.WithAuthInitialRoles(servex.UserRole("user")))

// Multiple default roles
server := servex.New(servex.WithAuthInitialRoles(
	servex.UserRole("user"),
	servex.UserRole("customer"),
))

Common role patterns:

  • Basic: "user"
  • Hierarchical: "user", "member", "premium"
  • Functional: "reader", "writer", "admin"

Users can have multiple roles. Additional roles can be assigned later through user management endpoints or database operations.

func WithAuthInitialUsers

func WithAuthInitialUsers(users ...InitialUser) Option

WithAuthInitialUsers creates initial users in the database when the server starts. This is useful for creating admin accounts or seeding the database with test users.

Example:

// Create admin user on startup
server := servex.New(
	servex.WithAuthMemoryDatabase(),
	servex.WithAuthInitialUsers(servex.InitialUser{
		Username: "admin",
		Password: "secure-admin-password",
		Roles:    []servex.UserRole{"admin", "user"},
	}),
)

// Multiple initial users
server := servex.New(
	servex.WithAuthMemoryDatabase(),
	servex.WithAuthInitialUsers(
		servex.InitialUser{
			Username: "admin",
			Password: "admin-pass",
			Roles:    []servex.UserRole{"admin"},
		},
		servex.InitialUser{
			Username: "testuser",
			Password: "test-pass",
			Roles:    []servex.UserRole{"user"},
		},
	),
)

Security considerations:

  • Use strong passwords
  • Consider loading from environment variables
  • Remove or change default passwords in production
  • Limit to essential accounts only

The users are created if they don't already exist in the database.

func WithAuthIssuer

func WithAuthIssuer(issuer string) Option

WithAuthIssuer sets the issuer name included in JWT token claims. This helps identify which service issued the token and can be used for validation.

Example:

// Set application name as issuer
server := servex.New(servex.WithAuthIssuer("my-api-service"))

// Environment-specific issuer
issuer := fmt.Sprintf("my-app-%s", os.Getenv("ENVIRONMENT"))
server := servex.New(servex.WithAuthIssuer(issuer))

The issuer appears in the JWT "iss" claim and can be verified by clients. Default is "testing" if not set.

Use descriptive names like:

  • Application name: "user-service", "payment-api"
  • Environment-specific: "my-app-prod", "my-app-staging"
  • Domain-based: "api.mycompany.com"

func WithAuthKey

func WithAuthKey(accessKey, refreshKey string) Option

WithAuthKey sets the JWT signing keys for access and refresh tokens. Keys should be hex-encoded strings. If empty, random keys will be generated.

Example:

// Use specific keys (recommended for production)
accessKey := "your-32-byte-hex-encoded-access-key"
refreshKey := "your-32-byte-hex-encoded-refresh-key"
server := servex.New(servex.WithAuthKey(accessKey, refreshKey))

// Generate random keys (development only)
server := servex.New(servex.WithAuthKey("", ""))

Key requirements:

  • Use strong, randomly generated keys
  • Access and refresh keys should be different
  • Store keys securely (environment variables, key management systems)
  • Rotate keys periodically in production

Security considerations:

  • Never hardcode keys in source code
  • Use environment variables or secure configuration
  • Different keys for different environments
  • Consider key rotation strategies

func WithAuthMemoryDatabase

func WithAuthMemoryDatabase() Option

WithAuthMemoryDatabase enables JWT authentication with an in-memory user database. This is convenient for development, testing, and applications that don't need persistent user data.

WARNING: All users and sessions will be lost when the application restarts. NOT RECOMMENDED FOR PRODUCTION USE.

Example:

// Development server with auth
server := servex.New(
	servex.WithAuthMemoryDatabase(),
	servex.WithAuthInitialUsers(servex.InitialUser{
		Username: "admin",
		Password: "admin123",
		Roles:    []servex.UserRole{"admin"},
	}),
)

This automatically registers the same endpoints as WithAuth().

Use this for:

  • Development and testing
  • Prototypes and demos
  • Applications with temporary users
  • Learning and experimentation

For production, implement a persistent database and use WithAuth() instead.

func WithAuthNotRegisterRoutes

func WithAuthNotRegisterRoutes(notRegisterRoutes bool) Option

WithAuthNotRegisterRoutes prevents automatic registration of default authentication routes. Use this when you want to implement custom authentication endpoints or integrate with existing authentication systems.

Example:

// Disable default auth routes
server := servex.New(
	servex.WithAuthMemoryDatabase(),
	servex.WithAuthNotRegisterRoutes(true),
)

// Register custom auth routes
server.HandleFunc("/custom/login", myCustomLoginHandler)
server.HandleFunc("/custom/register", myCustomRegisterHandler)

When enabled, you must implement your own:

  • User registration endpoint
  • Login endpoint
  • Token refresh endpoint
  • Logout endpoint
  • User profile endpoint

You can still use the AuthManager methods for token generation and validation. This gives you full control over request/response formats and business logic.

func WithAuthRefreshTokenCookieName

func WithAuthRefreshTokenCookieName(name string) Option

WithAuthRefreshTokenCookieName sets the name of the HTTP cookie used to store refresh tokens. The refresh token cookie is httpOnly and secure, providing protection against XSS attacks.

Example:

// Custom cookie name
server := servex.New(servex.WithAuthRefreshTokenCookieName("_my_refresh_token"))

// Short name for bandwidth
server := servex.New(servex.WithAuthRefreshTokenCookieName("_rt"))

Default is "_servexrt" if not set.

Cookie characteristics:

  • HttpOnly: Cannot be accessed by JavaScript
  • Secure: Only sent over HTTPS (in production)
  • SameSite: Protection against CSRF attacks
  • Expires: Set to refresh token duration

Choose names that don't conflict with your application's other cookies.

func WithAuthToken

func WithAuthToken(t string) Option

WithAuthToken enables simple token-based authentication using the Authorization header. When set, the server will check for "Authorization: Bearer <token>" headers on protected routes and compare against this token.

Example:

// Enable simple token auth
server := servex.New(servex.WithAuthToken("my-secret-api-key"))

// Client usage:
// curl -H "Authorization: Bearer my-secret-api-key" http://localhost:8080/api/protected

Use this for:

  • Simple API authentication
  • Service-to-service communication
  • Development and testing

For more advanced authentication with user management, JWT tokens, and roles, use WithAuth() or WithAuthMemoryDatabase() instead.

Note: This is a simple string comparison. For production use with multiple users or complex authorization, consider using the full JWT authentication system.

func WithAuthTokensDuration

func WithAuthTokensDuration(accessDuration, refreshDuration time.Duration) Option

WithAuthTokensDuration sets the validity duration for access and refresh tokens. Access tokens should be short-lived for security, while refresh tokens can be longer.

Example:

// Typical web application
server := servex.New(servex.WithAuthTokensDuration(
	15*time.Minute,  // Access token: 15 minutes
	7*24*time.Hour,  // Refresh token: 7 days
))

// High-security application
server := servex.New(servex.WithAuthTokensDuration(
	5*time.Minute,   // Access token: 5 minutes
	24*time.Hour,    // Refresh token: 1 day
))

// Development environment
server := servex.New(servex.WithAuthTokensDuration(
	1*time.Hour,     // Access token: 1 hour
	30*24*time.Hour, // Refresh token: 30 days
))

Recommended patterns:

  • Web apps: 15-60 min access, 7-30 days refresh
  • APIs: 5-30 min access, 1-7 days refresh
  • Mobile apps: 30-60 min access, 30-90 days refresh
  • High security: 5-15 min access, 1-3 days refresh

Shorter access tokens improve security but require more refresh operations.

func WithBlockedHeaders

func WithBlockedHeaders(headers map[string][]string) Option

WithBlockedHeaders blocks requests based on header values. Requests with headers matching the specified exact values will be denied.

Example:

// Block suspicious headers
server := servex.New(servex.WithBlockedHeaders(map[string][]string{
	"X-Forwarded-For": {"malicious-proxy-ip"},
	"User-Agent":      {"BadBot/1.0"},
}))

// Block old API versions
server := servex.New(servex.WithBlockedHeaders(map[string][]string{
	"X-API-Version": {"v0.1", "v0.2"},
}))

Header matching:

  • Header names are case-insensitive
  • Values must match exactly (case-sensitive)
  • Multiple blocked values per header
  • Any matching header causes blocking

Use cases:

  • Block deprecated API versions
  • Security header filtering
  • Malicious request detection
  • Legacy client blocking

Note: BlockedHeaders takes precedence over AllowedHeaders.

func WithBlockedHeadersRegex

func WithBlockedHeadersRegex(headers map[string][]string) Option

WithBlockedHeadersRegex blocks requests based on header regex patterns. Requests with headers matching the specified patterns will be denied.

Example:

// Block requests with suspicious X-Forwarded-For
server := servex.New(servex.WithBlockedHeadersRegex(map[string][]string{
	"X-Forwarded-For": {`(10\.0\.0\.|192\.168\.)`},  // Block internal IPs
}))

// Block old user agents
server := servex.New(servex.WithBlockedHeadersRegex(map[string][]string{
	"User-Agent": {`(?i)(bot|crawler|spider)`},
}))

Regex features:

  • Header names are case-insensitive
  • (?i) for case-insensitive pattern matching
  • Use standard Go regex syntax
  • Multiple patterns per header (OR logic)

Note: BlockedHeadersRegex takes precedence over AllowedHeadersRegex.

func WithBlockedIPs

func WithBlockedIPs(ips ...string) Option

WithBlockedIPs blocks access from specific IP addresses or CIDR ranges. Requests from these IPs will be denied with a 403 Forbidden response.

Example:

// Block known malicious IPs
server := servex.New(servex.WithBlockedIPs(
	"203.0.113.0/24",    // Known spam network
	"198.51.100.50",     // Specific malicious IP
	"192.0.2.0/24",      // Blocked range
))

// Block competitors from scraping
server := servex.New(servex.WithBlockedIPs("competitor-ip-range"))

IP formats supported:

  • Single IP: "192.168.1.100"
  • CIDR range: "10.0.0.0/8", "192.168.1.0/24"
  • IPv6: "2001:db8::1", "2001:db8::/32"

Use cases:

  • Block known malicious IPs
  • Prevent competitor scraping
  • Geographic restrictions
  • Temporary IP bans

Note: BlockedIPs takes precedence over AllowedIPs. If an IP is in both lists, it will be blocked.

func WithBlockedQueryParams

func WithBlockedQueryParams(params map[string][]string) Option

WithBlockedQueryParams blocks requests based on query parameter values. Requests with query parameters matching the specified exact values will be denied.

Example:

// Block dangerous parameters
server := servex.New(servex.WithBlockedQueryParams(map[string][]string{
	"debug": {"true", "1"},
	"admin": {"true", "1"},
}))

// Block SQL injection attempts
server := servex.New(servex.WithBlockedQueryParams(map[string][]string{
	"id": {"'; DROP TABLE users; --"},
}))

Parameter matching:

  • Parameter names are case-sensitive
  • Values must match exactly (case-sensitive)
  • Multiple blocked values per parameter
  • Any matching parameter causes blocking

Use cases:

  • Security parameter filtering
  • Debug mode blocking in production
  • Malicious query detection
  • Legacy parameter deprecation

Note: BlockedQueryParams takes precedence over AllowedQueryParams.

func WithBlockedQueryParamsRegex

func WithBlockedQueryParamsRegex(params map[string][]string) Option

WithBlockedQueryParamsRegex blocks requests based on query parameter regex patterns. Requests with query parameters matching the specified patterns will be denied.

Example:

// Block SQL injection patterns
server := servex.New(servex.WithBlockedQueryParamsRegex(map[string][]string{
	"search": {`(?i)(union|select|drop|delete|insert|update)`},
}))

// Block script injection
server := servex.New(servex.WithBlockedQueryParamsRegex(map[string][]string{
	"callback": {`(?i)(<script|javascript:|vbscript:)`},
}))

// Block excessive length
server := servex.New(servex.WithBlockedQueryParamsRegex(map[string][]string{
	"query": {`.{1000,}`},  // Block queries longer than 1000 chars
}))

Regex features:

  • Parameter names are case-sensitive
  • (?i) for case-insensitive pattern matching
  • Use standard Go regex syntax
  • Multiple patterns per parameter (OR logic)

Note: BlockedQueryParamsRegex takes precedence over AllowedQueryParamsRegex.

func WithBlockedUserAgents

func WithBlockedUserAgents(userAgents ...string) Option

WithBlockedUserAgents blocks access from specific User-Agent strings. Requests with these exact User-Agent headers will be denied.

Example:

// Block common bots
server := servex.New(servex.WithBlockedUserAgents(
	"Googlebot",
	"Bingbot",
	"facebookexternalhit",
	"Twitterbot",
))

// Block scrapers
server := servex.New(servex.WithBlockedUserAgents(
	"curl/7.68.0",
	"wget",
	"python-requests",
	"scrapy",
))

For pattern matching instead of exact strings, use WithBlockedUserAgentsRegex().

Use cases:

  • Block automated scrapers
  • Prevent bot traffic
  • Block specific tools
  • Temporary user-agent bans

Note: BlockedUserAgents takes precedence over AllowedUserAgents.

func WithBlockedUserAgentsRegex

func WithBlockedUserAgentsRegex(patterns ...string) Option

WithBlockedUserAgentsRegex blocks access using User-Agent regex patterns. Requests with User-Agent headers matching these patterns will be denied.

Example:

// Block all bots and crawlers
server := servex.New(servex.WithBlockedUserAgentsRegex(
	`(?i)(bot|crawler|spider|scraper)`,
))

// Block command line tools
server := servex.New(servex.WithBlockedUserAgentsRegex(
	`^(curl|wget|python-requests)`,
))

// Block old browser versions
server := servex.New(servex.WithBlockedUserAgentsRegex(
	`MSIE [1-9]\.`,  // IE 9 and below
))

Regex features:

  • (?i) for case-insensitive matching
  • Use standard Go regex syntax
  • ^ and $ for exact matching
  • | for alternatives

Note: BlockedUserAgentsRegex takes precedence over AllowedUserAgentsRegex.

func WithBurstSize

func WithBurstSize(burstSize int) Option

WithBurstSize sets the maximum burst size for rate limiting. This allows clients to exceed the normal rate limit temporarily by "bursting".

Example:

// 10 RPS with burst of 50 requests
server := servex.New(
	servex.WithRPS(10),
	servex.WithBurstSize(50),
)

// No bursting allowed
server := servex.New(
	servex.WithRPS(10),
	servex.WithBurstSize(1),
)

How it works:

  • Clients can make up to burstSize requests immediately
  • After bursting, they must wait for tokens to refill
  • Tokens refill at the configured rate (RPS/RPM)

Use cases:

  • Handle traffic spikes gracefully
  • Allow batch operations
  • Improve user experience for bursty clients
  • Balance performance with protection

If not set, defaults to the requests per interval value.

func WithCORS added in v2.2.0

func WithCORS() Option

WithCORS enables CORS with permissive defaults suitable for development. This allows all origins, methods, and headers with credentials disabled.

Example:

// Enable CORS with permissive defaults
server := servex.New(servex.WithCORS())

// Equivalent to:
server := servex.New(servex.WithCORSConfig(servex.CORSConfig{
	Enabled: true,
	AllowOrigins: []string{"*"},
	AllowMethods: []string{GET, POST, PUT, DELETE, OPTIONS},
	AllowHeaders: []string{"Content-Type", "Authorization", "X-Requested-With"},
	AllowCredentials: false,
	MaxAge: 3600,
}))

Security considerations:

  • This is permissive and suitable for development
  • For production, use specific origins with WithCORSAllowOrigins()
  • Never use this with credentials in production

For production use, configure specific origins:

server := servex.New(
	servex.WithCORS(),
	servex.WithCORSAllowOrigins("https://myapp.com"),
	servex.WithCORSAllowCredentials(true),
)

func WithCORSAllowCredentials added in v2.2.0

func WithCORSAllowCredentials() Option

WithCORSAllowCredentials enables sending credentials (cookies, auth headers) in CORS requests. When enabled, browsers will include credentials in cross-origin requests.

Example:

// Enable credentials for authenticated API
server := servex.New(
	servex.WithCORSAllowOrigins("https://app.example.com"),
	servex.WithCORSAllowCredentials(true),
)

// Session-based authentication with cookies
server := servex.New(
	servex.WithCORSAllowOrigins("https://frontend.example.com"),
	servex.WithCORSAllowCredentials(true),
	servex.WithCORSAllowHeaders("Content-Type", "X-CSRF-Token"),
)

Security requirements when enabled:

  • Cannot use "*" for AllowOrigins (must specify exact origins)
  • Increases CSRF attack surface (implement CSRF protection)
  • Consider implementing additional security measures

Use cases:

  • Session-based authentication with cookies
  • APIs that require Authorization headers
  • Single sign-on (SSO) systems
  • Applications with cross-domain user sessions

This automatically enables CORS if not already enabled.

func WithCORSAllowHeaders added in v2.2.0

func WithCORSAllowHeaders(headers ...string) Option

WithCORSAllowHeaders sets the allowed headers for CORS requests. This specifies which headers can be sent in cross-origin requests.

Example:

// API with authentication and custom headers
server := servex.New(servex.WithCORSAllowHeaders(
	"Content-Type",
	"Authorization",
	"X-API-Key",
	"X-Requested-With",
))

// Basic web application headers
server := servex.New(servex.WithCORSAllowHeaders(
	"Content-Type",
	"Authorization",
	"X-CSRF-Token",
))

// Allow all headers (less secure but convenient for development)
server := servex.New(servex.WithCORSAllowHeaders("*"))

Common header combinations:

  • Basic API: Content-Type, Authorization
  • Web app: Content-Type, Authorization, X-Requested-With, X-CSRF-Token
  • File upload: Content-Type, Authorization, X-Filename
  • Custom API: Content-Type, Authorization, X-API-Key, X-Client-Version

Standard headers that don't need explicit allowance:

  • Accept, Accept-Language, Content-Language
  • Content-Type (for simple values)

This automatically enables CORS if not already enabled.

func WithCORSAllowMethods added in v2.2.0

func WithCORSAllowMethods(methods ...string) Option

WithCORSAllowMethods sets the allowed HTTP methods for CORS requests. This specifies which HTTP methods are allowed in cross-origin requests.

Example:

// Full REST API support
server := servex.New(servex.WithCORSAllowMethods(
	GET, POST, PUT, DELETE, PATCH, OPTIONS,
))

// Read-only API
server := servex.New(servex.WithCORSAllowMethods(GET, "HEAD", OPTIONS))

// Create and read operations only
server := servex.New(servex.WithCORSAllowMethods(GET, POST, OPTIONS))

Common method combinations:

  • REST API: GET, POST, PUT, DELETE, PATCH, OPTIONS
  • Read-only: GET, HEAD, OPTIONS
  • Read/Create: GET, POST, OPTIONS
  • File API: GET, POST, PUT, DELETE, OPTIONS

Notes:

  • OPTIONS is automatically handled for preflight requests
  • GET and HEAD are "simple" methods that don't trigger preflight
  • Other methods (POST, PUT, DELETE, PATCH) trigger preflight requests

This automatically enables CORS if not already enabled.

func WithCORSAllowOrigins added in v2.2.0

func WithCORSAllowOrigins(origins ...string) Option

WithCORSAllowOrigins sets the allowed origins for CORS requests. This specifies which domains are allowed to make cross-origin requests to your server.

Example:

// Allow specific origins
server := servex.New(servex.WithCORSAllowOrigins(
	"https://myapp.com",
	"https://admin.myapp.com",
))

// Development setup with local origins
server := servex.New(servex.WithCORSAllowOrigins(
	"http://localhost:3000",
	"http://localhost:8080",
	"https://dev.myapp.com",
))

// Production API serving multiple frontends
server := servex.New(servex.WithCORSAllowOrigins(
	"https://app.example.com",
	"https://admin.example.com",
	"https://mobile.example.com",
))

Security best practices:

  • Always specify exact origins in production
  • Include protocol (https://) and port if non-standard
  • Never use "*" with credentials enabled
  • Use environment variables for different environments

This automatically enables CORS if not already enabled.

func WithCORSConfig added in v2.2.0

func WithCORSConfig(cors CORSConfig) Option

WithCORSConfig sets the complete CORS configuration. This allows fine-grained control over all CORS settings at once.

Example:

corsConfig := servex.CORSConfig{
	Enabled: true,
	AllowOrigins: []string{"https://example.com", "https://app.example.com"},
	AllowMethods: []string{GET, POST, PUT, DELETE, OPTIONS},
	AllowHeaders: []string{"Content-Type", "Authorization"},
	AllowCredentials: true,
	MaxAge: 3600,
}

server := servex.New(servex.WithCORSConfig(corsConfig))

Use this when you need to configure multiple CORS settings at once or when loading configuration from files or environment variables.

func WithCORSExcludePaths added in v2.2.0

func WithCORSExcludePaths(paths ...string) Option

WithCORSExcludePaths sets paths that should be excluded from CORS headers. Requests to these paths will not have CORS headers applied.

Example:

// Exclude internal and admin endpoints
server := servex.New(
	servex.WithCORS(),
	servex.WithCORSExcludePaths("/internal/*", "/admin/*"),
)

// Exclude non-browser endpoints
server := servex.New(
	servex.WithCORS(),
	servex.WithCORSExcludePaths("/webhooks/*", "/api/internal/*"),
)

Common exclusions:

  • Internal APIs: "/internal/*", "/private/*"
  • Admin interfaces: "/admin/*", "/management/*"
  • Webhooks: "/webhooks/*", "/callbacks/*"
  • Health checks: "/health", "/ping", "/metrics"
  • Server-to-server: "/api/internal/*"

Path matching supports wildcards (*) for pattern matching. Use when different endpoints need different CORS policies or when some endpoints should not be accessible to browsers.

This automatically enables CORS if not already enabled.

func WithCORSExposeHeaders added in v2.2.0

func WithCORSExposeHeaders(headers ...string) Option

WithCORSExposeHeaders sets which response headers are exposed to client-side JavaScript. This allows browsers to access specific response headers in cross-origin requests.

Example:

// Expose pagination headers
server := servex.New(servex.WithCORSExposeHeaders(
	"X-Total-Count",
	"X-Page-Count",
	"X-Per-Page",
))

// Expose file download headers
server := servex.New(servex.WithCORSExposeHeaders(
	"Content-Length",
	"Content-Range",
	"Content-Disposition",
))

// Expose custom API metadata
server := servex.New(servex.WithCORSExposeHeaders(
	"X-Rate-Limit-Remaining",
	"X-Rate-Limit-Reset",
	"Location",
))

Common headers to expose:

  • Pagination: X-Total-Count, X-Page-Count, X-Per-Page
  • Rate limiting: X-Rate-Limit-Remaining, X-Rate-Limit-Reset
  • File operations: Content-Length, Content-Range, Content-Disposition
  • Resource creation: Location
  • API metadata: X-API-Version, X-Request-ID

This automatically enables CORS if not already enabled.

func WithCORSIncludePaths added in v2.2.0

func WithCORSIncludePaths(paths ...string) Option

WithCORSIncludePaths sets paths that should have CORS headers applied. If set, only requests to these paths will receive CORS headers.

Example:

// CORS only for API endpoints
server := servex.New(
	servex.WithCORS(),
	servex.WithCORSIncludePaths("/api/*"),
)

// CORS for specific services
server := servex.New(
	servex.WithCORS(),
	servex.WithCORSIncludePaths("/api/public/*", "/auth/*"),
)

// CORS for user-facing endpoints only
server := servex.New(
	servex.WithCORS(),
	servex.WithCORSIncludePaths("/app/*", "/public/*"),
)

Use cases:

  • Mixed application: Only API endpoints need CORS
  • Gradual CORS adoption: Start with specific endpoints
  • Security: Limit CORS to necessary endpoints only
  • Performance: Reduce header overhead on internal endpoints

If both IncludePaths and ExcludePaths are set:

  1. Paths must match IncludePaths to receive CORS headers
  2. Paths in ExcludePaths are then excluded from CORS headers

Path matching supports wildcards (*) for pattern matching. Leave empty to apply CORS to all paths (default behavior).

This automatically enables CORS if not already enabled.

func WithCORSMaxAge added in v2.2.0

func WithCORSMaxAge(seconds int) Option

WithCORSMaxAge sets how long browsers can cache CORS preflight responses. This reduces the number of preflight requests by caching the CORS policy.

Example:

// Cache for 1 hour (good for development)
server := servex.New(servex.WithCORSMaxAge(3600))

// Cache for 1 day (good for production)
server := servex.New(servex.WithCORSMaxAge(86400))

// No caching (force preflight for every request)
server := servex.New(servex.WithCORSMaxAge(0))

// Cache for 1 week (very stable API)
server := servex.New(servex.WithCORSMaxAge(604800))

Common values:

  • Development: 3600 (1 hour) - allows quick policy changes
  • Production: 86400 (1 day) - good balance of performance and flexibility
  • Stable API: 604800 (1 week) - maximum performance
  • Testing: 0 - no caching for immediate policy changes

Benefits of longer caching:

  • Fewer preflight requests (better performance)
  • Reduced server load
  • Better user experience

Benefits of shorter caching:

  • Policy changes take effect quickly
  • Better for development and testing
  • More responsive to security updates

This automatically enables CORS if not already enabled.

func WithCSRFCookieHttpOnly

func WithCSRFCookieHttpOnly(httpOnly bool) Option

WithCSRFCookieHttpOnly sets whether the CSRF cookie is HTTP-only.

Example:

// Maximum security (recommended for server-side apps)
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFCookieHttpOnly(true),
	servex.WithCSRFTokenEndpoint("/csrf-token"),
)

// JavaScript accessible (for SPAs)
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFCookieHttpOnly(false),
)

Security considerations:

  • true: More secure, prevents XSS token theft, requires server-side token injection
  • false: JavaScript can read the token, but vulnerable to XSS attacks

When HttpOnly is true:

  • Use WithCSRFTokenEndpoint() to provide tokens to JavaScript
  • Inject tokens into HTML templates server-side
  • Maximum protection against XSS token theft

When HttpOnly is false:

  • JavaScript can read document.cookie to get the token
  • Useful for SPAs and AJAX-heavy applications
  • Consider additional XSS protections

func WithCSRFCookieMaxAge

func WithCSRFCookieMaxAge(maxAge int) Option

WithCSRFCookieMaxAge sets the maximum age for the CSRF cookie in seconds.

Example:

// Short-lived session (1 hour)
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFCookieMaxAge(3600),
)

// Daily session (24 hours)
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFCookieMaxAge(86400),
)

// Session cookie (expires when browser closes)
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFCookieMaxAge(0),
)

Common values:

  • 3600: 1 hour (short-lived, more secure)
  • 86400: 1 day (balance of security and usability)
  • 604800: 1 week (longer sessions)
  • 0: Session cookie (expires when browser closes)

Shorter durations improve security but may affect user experience. Choose based on your application's session management requirements.

func WithCSRFCookieName

func WithCSRFCookieName(cookieName string) Option

WithCSRFCookieName sets the name for the CSRF cookie.

Example:

// Use standard cookie name
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFCookieName("csrf_token"),
)

// Use Angular style (readable by JavaScript)
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFCookieName("XSRF-TOKEN"),
	servex.WithCSRFCookieHttpOnly(false),
)

// Use Express.js style
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFCookieName("_csrf"),
)

Common cookie names:

  • "csrf_token": Standard, secure
  • "XSRF-TOKEN": Angular compatible
  • "_csrf": Express.js style
  • "csrftoken": Django style

Choose names that don't conflict with your application's other cookies.

func WithCSRFCookiePath

func WithCSRFCookiePath(path string) Option

WithCSRFCookiePath sets the path attribute for the CSRF cookie.

Example:

// Cookie available for entire site
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFCookiePath("/"),
)

// Cookie only for application section
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFCookiePath("/app"),
)

// Cookie only for API endpoints
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFCookiePath("/api"),
)

Common paths:

  • "/": Cookie available for entire site (default)
  • "/app": Cookie only for application section
  • "/api": Cookie only for API endpoints

Use specific paths to limit cookie scope and improve security. The cookie will only be sent for requests under the specified path.

func WithCSRFCookieSameSite

func WithCSRFCookieSameSite(sameSite string) Option

WithCSRFCookieSameSite sets the SameSite attribute for the CSRF cookie.

Example:

// Maximum protection (may break some legitimate usage)
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFCookieSameSite("Strict"),
)

// Balanced protection (recommended)
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFCookieSameSite("Lax"),
)

// Cross-site requests allowed (requires Secure=true)
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFCookieSameSite("None"),
	servex.WithCSRFCookieSecure(true),
)

SameSite options:

  • "Strict": Maximum protection, blocks all cross-site requests
  • "Lax": Good protection with better usability (recommended)
  • "None": Allows cross-site requests, requires Secure=true

"Lax" provides good CSRF protection while maintaining usability for most applications.

func WithCSRFCookieSecure

func WithCSRFCookieSecure(secure bool) Option

WithCSRFCookieSecure sets whether the CSRF cookie requires HTTPS.

Example:

// Production HTTPS setup
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFCookieSecure(true),
)

// Development HTTP setup
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFCookieSecure(false),
)

Security recommendations:

  • true: Required for production HTTPS sites
  • false: Only for development with HTTP

The Secure flag is automatically set to true when SameSite="None". For production applications, always use HTTPS and set this to true.

func WithCSRFErrorMessage

func WithCSRFErrorMessage(message string) Option

WithCSRFErrorMessage sets the message returned when CSRF validation fails.

Example:

// Generic message (recommended for security)
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFErrorMessage("Invalid request. Please refresh and try again."),
)

// More specific message
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFErrorMessage("CSRF token missing or invalid"),
)

// User-friendly message
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFErrorMessage("Security validation failed. Please reload the page."),
)

Best practices:

  • Keep messages generic to avoid information disclosure
  • Include guidance for legitimate users
  • Consider localization for international applications
  • Avoid revealing technical implementation details

The message is returned as plain text in the response body with a 403 Forbidden status.

func WithCSRFProtection

func WithCSRFProtection() Option

WithCSRFProtection enables CSRF (Cross-Site Request Forgery) protection with default settings. This provides protection against CSRF attacks for web applications.

Example:

// Enable CSRF protection with defaults
server := servex.New(servex.WithCSRFProtection())

// Combined with other security features
server := servex.New(
	servex.WithStrictSecurityHeaders(),
	servex.WithCSRFProtection(),
)

Default settings:

  • Token name: "X-CSRF-Token"
  • Cookie name: "csrf_token"
  • Cookie HttpOnly: true (recommended for security)
  • Cookie SameSite: "Lax"
  • Safe methods: GET, HEAD, OPTIONS, TRACE

Use cases:

  • Web applications with forms
  • Single Page Applications (SPAs)
  • Any application accepting requests from browsers
  • APIs that need CSRF protection

For custom CSRF settings, use WithCSRFConfig() instead.

func WithCSRFSafeMethods

func WithCSRFSafeMethods(methods ...string) Option

WithCSRFSafeMethods sets the HTTP methods that bypass CSRF validation.

Example:

// Default safe methods (recommended)
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFSafeMethods(GET, "HEAD", OPTIONS, "TRACE"),
)

// More restrictive (only GET and HEAD)
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFSafeMethods(GET, "HEAD"),
)

// Allow additional methods (use with caution)
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFSafeMethods(GET, "HEAD", OPTIONS, "TRACE", "PROPFIND"),
)

Default safe methods: GET, HEAD, OPTIONS, TRACE

These methods are considered safe because they shouldn't have side effects. All other methods (POST, PUT, PATCH, DELETE) will require CSRF tokens.

Only modify this if you have specific requirements or use non-standard HTTP methods. Adding methods like POST to safe methods defeats the purpose of CSRF protection.

func WithCSRFTokenEndpoint

func WithCSRFTokenEndpoint(endpoint string) Option

WithCSRFTokenEndpoint enables an endpoint to retrieve CSRF tokens via AJAX.

Example:

// Standard CSRF token endpoint
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFTokenEndpoint("/csrf-token"),
)

// Custom endpoint path
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFTokenEndpoint("/api/csrf"),
)

// For SPAs with HttpOnly cookies
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFCookieHttpOnly(true),
	servex.WithCSRFTokenEndpoint("/api/v1/csrf-token"),
)

The endpoint will:

  • Use GET method
  • Return JSON: {"csrf_token": "abc123..."}
  • Set the CSRF cookie
  • Bypass CSRF validation (safe since it's read-only)

Use cases:

  • SPAs that need to fetch tokens dynamically
  • AJAX applications with HttpOnly cookies
  • Mobile apps that need CSRF tokens
  • Dynamic forms that load after page load

func WithCSRFTokenName

func WithCSRFTokenName(tokenName string) Option

WithCSRFTokenName sets the name for the CSRF token in headers and form fields.

Example:

// Use Rails/Django style token name
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFTokenName("X-CSRF-Token"),
)

// Use Angular style token name
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFTokenName("X-XSRF-TOKEN"),
)

// Use form field name
server := servex.New(
	servex.WithCSRFProtection(),
	servex.WithCSRFTokenName("csrf_token"),
)

Common token names:

  • "X-CSRF-Token": Rails, Django, standard header
  • "X-XSRF-TOKEN": Angular default
  • "csrf_token": Common form field name
  • "_token": Laravel style

The middleware will look for the token in:

  1. Request header with this name
  2. Form field with this name
  3. URL query parameter with this name (fallback)

func WithCacheAPI

func WithCacheAPI(maxAgeSeconds int) Option

WithCacheAPI sets up cache control for API endpoints with the specified max age. This applies cache headers optimized for API responses. The cache control will be set to "public, max-age=<maxAgeSeconds>". Recommended for stable API responses that don't change frequently.

func WithCacheConfig

func WithCacheConfig(cache CacheConfig) Option

WithCacheConfig sets the cache control configuration for HTTP responses. This allows you to configure all cache-related settings at once.

Example:

cacheConfig := servex.CacheConfig{
	Enabled:      true,
	CacheControl: "public, max-age=3600",
	Vary:         "Accept-Encoding",
}
server := servex.New(servex.WithCacheConfig(cacheConfig))

Use this when you need to configure multiple cache settings or when loading configuration from external sources like config files.

func WithCacheControl

func WithCacheControl(cacheControl string) Option

WithCacheControl enables cache control headers and sets the Cache-Control header value. This is the most common way to enable basic caching.

Example:

// Cache static assets for 1 hour
server := servex.New(servex.WithCacheControl("public, max-age=3600"))

// Disable caching for sensitive data
server := servex.New(servex.WithCacheControl("no-store"))

// Private cache for user-specific content
server := servex.New(servex.WithCacheControl("private, max-age=900"))

Common Cache-Control values:

  • "no-cache": Must revalidate before using cached copy
  • "no-store": Do not cache at all (sensitive data)
  • "public, max-age=3600": Public cache for 1 hour
  • "private, max-age=900": Private cache for 15 minutes
  • "public, max-age=31536000, immutable": Cache for 1 year (static assets)

func WithCacheETag

func WithCacheETag(etag string) Option

WithCacheETag sets the ETag header for cache validation. ETags allow clients to validate cached content without downloading.

Example:

// Static ETag based on content version
server := servex.New(servex.WithCacheETag(`"v1.2.3"`))

// Weak ETag based on timestamp
server := servex.New(servex.WithCacheETag(`W/"Tue, 15 Nov 1994 12:45:26 GMT"`))

// Combined with Cache-Control
server := servex.New(
	servex.WithCacheControl("public, max-age=0, must-revalidate"),
	servex.WithCacheETag(`"33a64df551"`),
)

ETag formats:

  • Strong ETag: `"version123"` (content identical)
  • Weak ETag: `W/"version123"` (content equivalent)

Use ETags when you want clients to validate cached content efficiently.

func WithCacheETagFunc

func WithCacheETagFunc(etagFunc func(r *http.Request) string) Option

WithCacheETagFunc sets a dynamic ETag generation function. The function is called for each request to generate request-specific ETags.

Example:

// Generate ETag based on user ID and content version
server := servex.New(servex.WithCacheETagFunc(func(r *http.Request) string {
	userID := getUserID(r)
	version := getContentVersion()
	return `"` + userID + "-" + version + `"`
}))

// Generate ETag based on request path
server := servex.New(servex.WithCacheETagFunc(func(r *http.Request) string {
	hash := sha256.Sum256([]byte(r.URL.Path))
	return `"` + hex.EncodeToString(hash[:8]) + `"`
}))

// Weak ETag based on timestamp
server := servex.New(servex.WithCacheETagFunc(func(r *http.Request) string {
	return `W/"` + time.Now().Format("20060102150405") + `"`
}))

Use for content that varies per request or needs dynamic validation.

func WithCacheExcludePaths

func WithCacheExcludePaths(paths ...string) Option

WithCacheExcludePaths sets paths that should be excluded from cache control headers. Requests to these paths will not have cache control headers applied.

Example:

// Exclude dynamic endpoints from caching
server := servex.New(
	servex.WithCacheControl("public, max-age=3600"),
	servex.WithCacheExcludePaths("/api/*", "/user/*", "/admin/*"),
)

// Exclude authentication and real-time endpoints
server := servex.New(
	servex.WithCacheHeaders(),
	servex.WithCacheExcludePaths("/auth/*", "/ws/*", "/stream/*"),
)

Common exclusions:

  • Dynamic APIs: "/api/*", "/graphql"
  • User-specific content: "/user/*", "/profile/*"
  • Authentication: "/auth/*", "/login", "/logout"
  • Admin interfaces: "/admin/*"
  • Real-time endpoints: "/ws/*", "/stream/*"

Path matching supports wildcards (*) for pattern matching.

func WithCacheExpires

func WithCacheExpires(expires string) Option

WithCacheExpires sets the Expires header for cache control. This provides a fallback for older HTTP/1.0 clients.

Example:

// Set expiration time
expireTime := time.Now().Add(1 * time.Hour).Format(http.TimeFormat)
server := servex.New(servex.WithCacheExpires(expireTime))

// Combined with Cache-Control
server := servex.New(
	servex.WithCacheControl("public, max-age=3600"),
	servex.WithCacheExpires(time.Now().Add(1*time.Hour).Format(http.TimeFormat)),
)

Note: Modern clients prefer Cache-Control over Expires. Use this only for compatibility with older clients or as a fallback.

func WithCacheExpiresTime

func WithCacheExpiresTime(expires time.Time) Option

WithCacheExpiresTime sets the Expires header using a time.Time value. This automatically formats the time using HTTP time format (RFC 7231).

Example:

// Set expiration time to 1 hour from now
server := servex.New(servex.WithCacheExpiresTime(time.Now().Add(time.Hour)))

// Set expiration to a specific time
expireTime := time.Date(2024, 12, 31, 23, 59, 59, 0, time.UTC)
server := servex.New(servex.WithCacheExpiresTime(expireTime))

// Combined with Cache-Control
server := servex.New(
	servex.WithCacheControl("public, max-age=3600"),
	servex.WithCacheExpiresTime(time.Now().Add(time.Hour)),
)

This is more convenient than WithCacheExpires() when working with time.Time values.

func WithCacheHeaders

func WithCacheHeaders() Option

WithCacheHeaders enables cache control headers with basic settings. This sets common cache control headers for typical web applications.

Example:

// Enable basic caching with common defaults
server := servex.New(servex.WithCacheHeaders())

This sets:

  • Cache-Control: "public, max-age=3600" (1 hour)
  • Vary: "Accept-Encoding" (for compression)

Use this for quick setup with sensible defaults. For custom settings, use WithCacheControl() or WithCacheConfig() instead.

func WithCacheIncludePaths

func WithCacheIncludePaths(paths ...string) Option

WithCacheIncludePaths sets paths that should have cache control headers applied. If set, only requests to these paths will receive cache control headers.

Example:

// Cache only static assets
server := servex.New(
	servex.WithCacheControl("public, max-age=31536000, immutable"),
	servex.WithCacheIncludePaths("/static/*", "/assets/*", "/images/*"),
)

// Cache specific API endpoints
server := servex.New(
	servex.WithCacheControl("public, max-age=300"),
	servex.WithCacheIncludePaths("/api/public/*", "/docs/*"),
)

Use cases:

  • Cache only static assets: "/static/*", "/assets/*"
  • Cache specific API endpoints: "/api/public/*"
  • Cache documentation: "/docs/*"

If both IncludePaths and ExcludePaths are set:

  1. Paths must match IncludePaths to receive cache headers
  2. Paths in ExcludePaths are then excluded from cache headers

Path matching supports wildcards (*) for pattern matching.

func WithCacheLastModified

func WithCacheLastModified(lastModified string) Option

WithCacheLastModified sets the Last-Modified header for cache validation. This indicates when the resource was last changed.

Example:

// Set last modified time
lastMod := time.Now().AddDate(0, 0, -1).Format(http.TimeFormat)
server := servex.New(servex.WithCacheLastModified(lastMod))

// Combined with Cache-Control
server := servex.New(
	servex.WithCacheControl("public, max-age=0, must-revalidate"),
	servex.WithCacheLastModified(time.Now().Format(http.TimeFormat)),
)

Benefits:

  • Enables conditional requests (If-Modified-Since)
  • Reduces bandwidth for unchanged resources
  • Works well with ETags for cache validation

func WithCacheLastModifiedFunc

func WithCacheLastModifiedFunc(lastModifiedFunc func(r *http.Request) time.Time) Option

WithCacheLastModifiedFunc sets a dynamic Last-Modified generation function. The function is called for each request to generate request-specific modification times.

Example:

// Get modification time from file system
server := servex.New(servex.WithCacheLastModifiedFunc(func(r *http.Request) time.Time {
	filePath := "./static" + r.URL.Path
	if info, err := os.Stat(filePath); err == nil {
		return info.ModTime()
	}
	return time.Now()
}))

// Get modification time from database
server := servex.New(servex.WithCacheLastModifiedFunc(func(r *http.Request) time.Time {
	resourceID := getResourceID(r)
	return getResourceModTime(resourceID)
}))

// Use current time for dynamic content
server := servex.New(servex.WithCacheLastModifiedFunc(func(r *http.Request) time.Time {
	return time.Now().Truncate(time.Minute) // Round to minute for better caching
}))

Use for content where modification time varies per request or resource.

func WithCacheLastModifiedTime

func WithCacheLastModifiedTime(lastModified time.Time) Option

WithCacheLastModifiedTime sets the Last-Modified header using a time.Time value. This automatically formats the time using HTTP time format (RFC 7231).

Example:

// Set last modified to file modification time
fileInfo, _ := os.Stat("static/app.js")
server := servex.New(servex.WithCacheLastModifiedTime(fileInfo.ModTime()))

// Set last modified to application start time
server := servex.New(servex.WithCacheLastModifiedTime(time.Now()))

// Combined with Cache-Control for conditional requests
server := servex.New(
	servex.WithCacheControl("public, max-age=0, must-revalidate"),
	servex.WithCacheLastModifiedTime(time.Now().AddDate(0, 0, -1)),
)

This is more convenient than WithCacheLastModified() when working with time.Time values.

func WithCacheNoCache

func WithCacheNoCache() Option

WithCacheNoCache enables cache control with no-cache directive. Forces caches to revalidate with the origin server before using cached content.

Example:

// API endpoints that change frequently
server := servex.New(servex.WithCacheNoCache())

// Combined with ETag for efficient revalidation
server := servex.New(
	servex.WithCacheNoCache(),
	servex.WithCacheETag(`"v1.2.3"`),
)

Use for:

  • API responses that may change
  • Dynamic content that should be revalidated
  • Content where freshness is important

This sets Cache-Control to "no-cache, must-revalidate".

func WithCacheNoStore

func WithCacheNoStore() Option

WithCacheNoStore disables all caching for sensitive content. Prevents any caching of the response by browsers, proxies, or CDNs.

Example:

// Sensitive user data
server := servex.New(servex.WithCacheNoStore())

// Apply only to sensitive endpoints
server := servex.New(
	servex.WithCacheNoStore(),
	servex.WithCacheIncludePaths("/api/private/*", "/user/settings"),
)

Use for:

  • Personal user data
  • Authentication endpoints
  • Payment information
  • Confidential content

This sets Cache-Control to "no-store, no-cache, must-revalidate".

func WithCachePrivate

func WithCachePrivate(maxAgeSeconds int) Option

WithCachePrivate enables private caching with the specified max-age in seconds. Allows only browsers to cache the content, not intermediary proxies or CDNs.

Example:

// Cache user-specific data for 15 minutes (900 seconds)
server := servex.New(servex.WithCachePrivate(900))

// Cache user profile for 5 minutes (300 seconds)
server := servex.New(servex.WithCachePrivate(300))

// Cache personalized content
server := servex.New(
	servex.WithCachePrivate(1800), // 30 minutes
	servex.WithCacheIncludePaths("/api/user/*"),
)

Use for:

  • User-specific content
  • Personalized responses
  • Content that varies by authentication
  • Semi-sensitive data

This sets Cache-Control to "private, max-age=<seconds>".

func WithCachePublic

func WithCachePublic(maxAgeSeconds int) Option

WithCachePublic enables public caching with the specified max-age in seconds. Allows both browsers and intermediary proxies/CDNs to cache the content.

Example:

// Cache static assets for 1 hour (3600 seconds)
server := servex.New(servex.WithCachePublic(3600))

// Cache API responses for 5 minutes (300 seconds)
server := servex.New(servex.WithCachePublic(300))

// Cache static assets for 1 year with immutable content
server := servex.New(
	servex.WithCachePublic(31536000), // 1 year
	servex.WithCacheIncludePaths("/static/*"),
)

Use for:

  • Static assets (CSS, JS, images)
  • Public API responses
  • Documentation
  • Content that doesn't vary by user

This sets Cache-Control to "public, max-age=<seconds>".

func WithCacheStaticAssets

func WithCacheStaticAssets(maxAgeSeconds int) Option

WithCacheStaticAssets enables optimized caching for static assets. Sets long-term public caching with immutable directive for maximum performance.

Example:

// Cache static assets for 1 year (default)
server := servex.New(
	servex.WithCacheStaticAssets(0), // Uses default 1 year
	servex.WithCacheIncludePaths("/static/*", "/assets/*"),
)

// Cache static assets for 6 months
server := servex.New(
	servex.WithCacheStaticAssets(15552000), // 6 months
	servex.WithCacheIncludePaths("/js/*", "/css/*", "/images/*"),
)

// Perfect for versioned static assets
server := servex.New(
	servex.WithCacheStaticAssets(0),
	servex.WithCacheIncludePaths("/static/v*/", "/assets/build/*"),
	servex.WithCacheVary("Accept-Encoding"),
)

Use for:

  • Versioned static files (CSS, JS, images)
  • Build artifacts with hashes in filenames
  • Content that never changes once deployed
  • CDN-optimized assets

This sets Cache-Control to "public, max-age=<seconds>, immutable". If maxAgeSeconds is 0, defaults to 31536000 (1 year).

func WithCacheVary

func WithCacheVary(vary string) Option

WithCacheVary sets the Vary header to specify which request headers affect caching. This tells caches that the response varies based on certain request headers.

Example:

// Content varies by compression
server := servex.New(servex.WithCacheVary("Accept-Encoding"))

// Content varies by multiple headers
server := servex.New(servex.WithCacheVary("Accept-Encoding, User-Agent, Accept-Language"))

// Combined with Cache-Control
server := servex.New(
	servex.WithCacheControl("public, max-age=3600"),
	servex.WithCacheVary("Accept-Encoding"),
)

Common Vary values:

  • "Accept-Encoding": Different compression formats
  • "User-Agent": Different responses for different browsers
  • "Accept": Different content types (JSON vs XML)
  • "Authorization": Different responses for authenticated users
  • "Accept-Language": Different languages

Important: Only include headers that actually affect the response to avoid cache fragmentation.

func WithCertificate

func WithCertificate(cert tls.Certificate) Option

WithCertificate sets the TLS certificate for the server from a pre-loaded tls.Certificate. This enables HTTPS support on the server. You must start the server with an HTTPS address for the certificate to be used.

Example:

cert, _ := tls.LoadX509KeyPair("server.crt", "server.key")
options.Certificate = &cert
server := servex.New(servex.WithCertificate(cert))
server.Start("", ":8443") // HTTPS only

Use this when you have already loaded the certificate in memory, perhaps for certificate rotation or when loading from embedded files.

func WithCertificateFromFile

func WithCertificateFromFile(certFilePath, keyFilePath string) Option

WithCertificateFromFile configures the server to load TLS certificate from files. This enables HTTPS support on the server. The certificate files will be loaded when the server starts. You must start the server with an HTTPS address for the certificate to be used.

Parameters:

  • certFilePath: Path to the PEM-encoded certificate file
  • keyFilePath: Path to the PEM-encoded private key file

Example:

// Load certificate from files
server := servex.New(servex.WithCertificateFromFile("server.crt", "server.key"))
server.Start(":8080", ":8443") // Both HTTP and HTTPS

// HTTPS only server
server := servex.New(servex.WithCertificateFromFile("cert.pem", "key.pem"))
server.Start("", ":8443") // HTTPS only

This is the most common way to configure TLS certificates. Ensure the files are readable by the application and contain valid PEM-encoded data.

func WithCertificatePtr

func WithCertificatePtr(cert *tls.Certificate) Option

WithCertificatePtr sets the TLS certificate for the server from a pointer to tls.Certificate. This enables HTTPS support on the server. You must start the server with an HTTPS address for the certificate to be used.

Example:

cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
	log.Fatal(err)
}
server := servex.New(servex.WithCertificatePtr(&cert))
server.Start("", ":8443") // HTTPS only

Use this when you need to pass a certificate pointer, useful when sharing certificate instances or when the certificate is managed externally.

func WithCompression added in v2.2.0

func WithCompression() Option

WithCompression enables HTTP response compression with sensible defaults. This automatically compresses text-based responses using gzip encoding.

Default configuration:

  • Compression level: 6 (balanced speed/compression)
  • Minimum size: 1KB
  • Types: HTML, CSS, JS, JSON, XML, plain text, SVG
  • All paths included unless specifically excluded

Example:

// Enable compression with defaults
server, _ := servex.New(servex.WithCompression())

// Enable compression with custom minimum size
server, _ := servex.New(
	servex.WithCompression(),
	servex.WithCompressionMinSize(512),
)

Benefits:

  • Reduces bandwidth usage by 60-80% for text content
  • Improves page load times
  • Lower hosting costs
  • Better user experience

func WithCompressionConfig added in v2.2.0

func WithCompressionConfig(compression CompressionConfig) Option

WithCompressionConfig sets the complete compression configuration. This provides full control over all compression settings.

Example:

compressionConfig := servex.CompressionConfig{
	Enabled: true,
	Level: 6,
	MinSize: 1024,
	Types: []string{"text/html", "application/json", "text/css"},
	ExcludePaths: []string{"/api/binary/*"},
}
server, _ := servex.New(servex.WithCompressionConfig(compressionConfig))

For simpler setups, consider using WithCompression() instead.

func WithCompressionExcludePaths added in v2.2.0

func WithCompressionExcludePaths(paths ...string) Option

WithCompressionExcludePaths sets paths that should be excluded from compression. Responses for these paths will not be compressed regardless of other settings.

Parameters:

  • paths: List of path patterns to exclude (supports wildcards with *)

Common exclusions:

  • "/api/binary/*": Binary API endpoints
  • "/downloads/*": File download endpoints
  • "/images/*": Image files (already compressed)
  • "/videos/*": Video files (already compressed)

Example:

// Exclude binary and media endpoints
server, _ := servex.New(
	servex.WithCompression(),
	servex.WithCompressionExcludePaths("/api/binary/*", "/downloads/*", "/media/*"),
)

Path matching supports wildcards (*) for pattern matching.

func WithCompressionIncludePaths added in v2.2.0

func WithCompressionIncludePaths(paths ...string) Option

WithCompressionIncludePaths sets paths that should have compression applied. If set, only responses for these paths will be compressed.

Parameters:

  • paths: List of path patterns to include (supports wildcards with *)

If both IncludePaths and ExcludePaths are set:

  1. Paths must match IncludePaths to be considered for compression
  2. Paths in ExcludePaths are then excluded from compression

Example:

// Compress only API and static assets
server, _ := servex.New(
	servex.WithCompression(),
	servex.WithCompressionIncludePaths("/api/*", "/static/*"),
)

// Compress specific content areas
server, _ := servex.New(
	servex.WithCompression(),
	servex.WithCompressionIncludePaths("/docs/*", "/help/*", "/blog/*"),
)

Path matching supports wildcards (*) for pattern matching. Leave empty to apply compression to all paths (default behavior).

func WithCompressionLevel added in v2.2.0

func WithCompressionLevel(level int) Option

WithCompressionLevel sets the compression level for gzip encoding. Higher levels provide better compression but use more CPU.

Valid range: 1-9

  • 1: Fastest compression, lower CPU usage
  • 6: Default balance (recommended)
  • 9: Best compression, higher CPU usage

Example:

// Fast compression for high-traffic servers
server, _ := servex.New(
	servex.WithCompression(),
	servex.WithCompressionLevel(1),
)

// Maximum compression for bandwidth-constrained environments
server, _ := servex.New(
	servex.WithCompression(),
	servex.WithCompressionLevel(9),
)

func WithCompressionMinSize added in v2.2.0

func WithCompressionMinSize(size int) Option

WithCompressionMinSize sets the minimum response size to trigger compression. Responses smaller than this size will not be compressed.

Parameters:

  • size: Minimum size in bytes (0 to compress all responses)

Example:

// Only compress responses larger than 2KB
server, _ := servex.New(
	servex.WithCompression(),
	servex.WithCompressionMinSize(2048),
)

// Compress all responses regardless of size
server, _ := servex.New(
	servex.WithCompression(),
	servex.WithCompressionMinSize(0),
)

Considerations:

  • Small responses may not benefit from compression
  • Compression adds CPU overhead
  • Network overhead for small responses may negate benefits

func WithCompressionTypes added in v2.2.0

func WithCompressionTypes(types ...string) Option

WithCompressionTypes sets the MIME types that should be compressed. Only responses with these content types will be compressed.

Parameters:

  • types: List of MIME types to compress

Common types:

  • "text/html": HTML pages
  • "text/css": CSS stylesheets
  • "application/javascript": JavaScript files
  • "application/json": JSON API responses
  • "text/xml": XML responses
  • "text/plain": Plain text files
  • "image/svg+xml": SVG images

Example:

// Compress only JSON and HTML
server, _ := servex.New(
	servex.WithCompression(),
	servex.WithCompressionTypes("application/json", "text/html"),
)

// Add additional types to defaults
server, _ := servex.New(
	servex.WithCompression(),
	servex.WithCompressionTypes("application/pdf", "text/csv"),
)

func WithContentSecurityPolicy

func WithContentSecurityPolicy(policy string) Option

WithContentSecurityPolicy sets the Content-Security-Policy header to prevent XSS attacks. CSP controls which resources (scripts, styles, images, etc.) can be loaded by the browser.

Example:

// Basic CSP allowing only same-origin resources
server := servex.New(servex.WithContentSecurityPolicy("default-src 'self'"))

// CSP allowing external CDNs
server := servex.New(servex.WithContentSecurityPolicy(
	"default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'",
))

// CSP for API-only server (no resources)
server := servex.New(servex.WithContentSecurityPolicy("default-src 'none'"))

Common CSP directives:

  • default-src: Default policy for all resource types
  • script-src: JavaScript sources
  • style-src: CSS sources
  • img-src: Image sources
  • connect-src: AJAX, WebSocket, EventSource sources
  • font-src: Font sources
  • object-src: Plugin sources (usually set to 'none')
  • media-src: Video/audio sources
  • frame-src: Iframe sources

Common values:

  • 'self': Same origin as the document
  • 'none': No resources allowed
  • 'unsafe-inline': Allow inline scripts/styles (not recommended)
  • 'unsafe-eval': Allow eval() (not recommended)
  • https://example.com: Specific domains

Security note: CSP is one of the most effective defenses against XSS attacks. Start with a restrictive policy and gradually allow necessary resources.

func WithCustomHeaders

func WithCustomHeaders(headers map[string]string) Option

WithCustomHeaders sets custom HTTP headers that will be added to all responses. These headers are applied after security headers and can override them.

Example:

// Add custom API headers
server := servex.New(servex.WithCustomHeaders(map[string]string{
	"X-API-Version": "v1.0",
	"X-Service-Name": "user-service",
	"X-Environment": "production",
}))

// Add caching headers
server := servex.New(servex.WithCustomHeaders(map[string]string{
	"Cache-Control": "no-cache, no-store, must-revalidate",
	"Pragma": "no-cache",
	"Expires": "0",
}))

// Add CORS headers (basic example)
server := servex.New(servex.WithCustomHeaders(map[string]string{
	"Access-Control-Allow-Origin": "*",
	"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
	"Access-Control-Allow-Headers": "Content-Type, Authorization",
}))

Use cases:

  • API versioning headers
  • Service identification
  • Custom caching policies
  • CORS configuration
  • Application-specific headers
  • Debugging and monitoring headers

Note: Custom headers can override security headers if they have the same name. For security headers, prefer using the dedicated security options instead.

func WithDefaultAuditLogger

func WithDefaultAuditLogger() Option

WithDefaultAuditLogger enables default audit logging using the server's logger. This creates a DefaultAuditLogger that logs security events using structured logging.

Example:

// Enable basic audit logging
server := servex.New(
	servex.WithDefaultAuditLogger(),
)

// Enable audit logging with headers included (be careful with sensitive data)
server := servex.New(
	servex.WithDefaultAuditLogger(),
	servex.WithAuditLogHeaders(true),
)

The default audit logger will:

  • Log authentication events (login, logout, token validation)
  • Log rate limiting violations
  • Log request filtering blocks (IP, User-Agent, etc.)
  • Log CSRF protection events
  • Log suspicious activities

All events are logged with structured fields for easy parsing and analysis.

func WithDefaultMetrics

func WithDefaultMetrics(path ...string) Option

WithDefaultMetrics enables the default built-in metrics endpoint with Prometheus-compatible output.

This creates a comprehensive metrics endpoint that tracks:

  • Total requests, responses, and errors
  • Error rate percentage
  • Average response time in milliseconds
  • Requests per second
  • Response status code distribution (2xx, 3xx, 4xx, 5xx)
  • HTTP method distribution (GET, POST, PUT, DELETE, etc.)
  • Per-path request counts and timing statistics
  • System metrics (memory usage, goroutine count, GC stats)

The metrics endpoint outputs in Prometheus text format (version 0.0.4) and can be scraped by Prometheus, Grafana, or other monitoring tools.

Example:

// Enable default metrics at /metrics
server := servex.New(servex.WithDefaultMetrics())

// Custom metrics path
server := servex.New(servex.WithDefaultMetrics("/stats"))

// Combined with other monitoring features
server := servex.New(
	servex.WithDefaultMetrics("/metrics"),
	servex.WithHealthEndpoint(),
	servex.WithRequestLogger(myLogger),
)

Available metrics include:

  • servex_requests_total - Total number of HTTP requests
  • servex_responses_total - Total number of HTTP responses
  • servex_errors_total - Total number of errors (4xx and 5xx)
  • servex_error_rate_percent - Percentage of requests that resulted in errors
  • servex_requests_per_second - Current request rate
  • servex_response_time_ms_avg - Average response time in milliseconds
  • servex_responses_by_status_total{status="200"} - Responses by status code
  • servex_requests_by_method_total{method="GET"} - Requests by HTTP method
  • servex_requests_by_path_total{path="/api/users"} - Requests by path
  • servex_memory_usage_bytes - Current memory usage
  • servex_goroutines - Number of active goroutines
  • servex_gc_count - Total garbage collection count

Default path: "/metrics" if not specified.

Use this when:

  • You want quick observability without writing custom metrics
  • You need Prometheus-compatible metrics
  • You want built-in system and HTTP metrics
  • You're building a production service and need monitoring

Note: This automatically creates and registers a metrics endpoint. For custom metrics, use WithMetrics() instead.

func WithDisableHealthEndpoint added in v2.3.0

func WithDisableHealthEndpoint() Option

WithDisableHealthEndpoint disables the automatic health check endpoint.

The health endpoint is enabled by default for production readiness. Use this function to explicitly disable it if you:

  • Don't need health checks
  • Have your own custom health check endpoint
  • Want to minimize exposed endpoints
  • Are running in an environment without load balancers

Example:

// Disable the default health endpoint
server := servex.New(servex.WithDisableHealthEndpoint())

// Disable and implement custom health logic
server := servex.New(servex.WithDisableHealthEndpoint())
server.Get("/custom-health", func(w http.ResponseWriter, r *http.Request) {
	// Custom health check logic
	servex.C(w, r).Response(200, map[string]string{"status": "healthy"})
})

Note: Most production applications should keep the health endpoint enabled for proper monitoring and orchestration.

func WithDisableRequestLogging

func WithDisableRequestLogging() Option

WithDisableRequestLogging disables HTTP request logging completely. This is an alias for WithNoRequestLog().

Example:

server := servex.New(servex.WithDisableRequestLogging())

See WithNoRequestLog() for detailed documentation.

func WithEnableRequestSizeLimits

func WithEnableRequestSizeLimits(enable bool) Option

WithEnableRequestSizeLimits enables global request size limit middleware. When enabled, all requests are checked against the configured size limits. Individual endpoints can still use smaller limits via context methods.

Use cases for disabling:

  • Fine-grained control per endpoint
  • Custom size limit middleware
  • Performance-critical applications
  • Legacy compatibility

func WithFilterConfig

func WithFilterConfig(filter FilterConfig) Option

WithFilterConfig sets the complete request filtering configuration at once. This allows fine-grained control over all filtering settings for IP addresses, User-Agents, headers, and query parameters.

Example:

filterConfig := servex.FilterConfig{
	AllowedIPs: []string{"10.0.0.0/8", "192.168.1.100"},
	BlockedUserAgents: []string{"BadBot", "Scraper"},
	AllowedHeaders: map[string][]string{
		"X-API-Version": {"v1", "v2"},
	},
	StatusCode: 403,
	Message: "Access denied by security filter",
	ExcludePaths: []string{"/health", "/public/*"},
}

server := servex.New(servex.WithFilterConfig(filterConfig))

Use this when you need to configure multiple filtering settings at once or when loading configuration from files or environment variables.

func WithFilterExcludePaths

func WithFilterExcludePaths(paths ...string) Option

WithFilterExcludePaths excludes specific paths from request filtering. Requests to these paths will bypass all filtering rules.

Example:

// Exclude public endpoints from filtering
server := servex.New(
	servex.WithAllowedIPs("192.168.1.0/24"),
	servex.WithFilterExcludePaths("/health", "/public/*", "/docs/*"),
)

// Exclude monitoring from strict filtering
server := servex.New(
	servex.WithBlockedUserAgents("curl"),
	servex.WithFilterExcludePaths("/metrics", "/status", "/ping"),
)

Common exclusions:

  • Health checks: "/health", "/ping"
  • Public APIs: "/public/*", "/api/public/*"
  • Documentation: "/docs/*", "/swagger/*"
  • Static assets: "/static/*", "/assets/*"
  • Monitoring: "/metrics", "/status"

Path matching supports wildcards (*) for pattern matching. Excluded paths bypass ALL filtering rules (IP, User-Agent, headers, query params).

func WithFilterIncludePaths

func WithFilterIncludePaths(paths ...string) Option

WithFilterIncludePaths specifies which paths should be filtered. If set, only requests to these paths will be subject to filtering rules.

Example:

// Only filter admin endpoints
server := servex.New(
	servex.WithAllowedIPs("192.168.1.0/24"),
	servex.WithFilterIncludePaths("/admin/*", "/api/admin/*"),
)

// Filter only sensitive API endpoints
server := servex.New(
	servex.WithBlockedUserAgents("curl", "wget"),
	servex.WithFilterIncludePaths("/api/sensitive/*", "/api/payment/*"),
)

If both IncludePaths and ExcludePaths are set:

  1. Paths must match IncludePaths to be filtered
  2. Paths in ExcludePaths are then excluded from filtering

Use cases:

  • Protect only sensitive endpoints
  • Apply filtering to specific API versions
  • Filter only external-facing endpoints
  • Granular security control

Path matching supports wildcards (*) for pattern matching.

func WithFilterMessage

func WithFilterMessage(message string) Option

WithFilterMessage sets the response message when requests are blocked by filters. Default is "Request blocked by security filter" if not set.

Example:

// Generic security message
server := servex.New(
	servex.WithAllowedIPs("192.168.1.0/24"),
	servex.WithFilterMessage("Access denied for security reasons"),
)

// Specific filter message
server := servex.New(
	servex.WithBlockedUserAgents("BadBot"),
	servex.WithFilterMessage("Your user agent is not allowed"),
)

// Helpful message with contact info
server := servex.New(
	servex.WithAllowedHeaders(map[string][]string{"X-API-Key": {"validkey"}}),
	servex.WithFilterMessage("Missing or invalid API key. Contact [email protected] for access."),
)

Best practices:

  • Be clear but not too specific about the filter
  • Include contact information for legitimate users
  • Avoid revealing security implementation details
  • Keep messages user-friendly

The message is returned as plain text in the response body.

func WithFilterStatusCode

func WithFilterStatusCode(statusCode int) Option

WithFilterStatusCode sets the HTTP status code returned when requests are blocked by filters. Default is 403 (Forbidden) if not set.

Example:

// Use standard 403 Forbidden
server := servex.New(
	servex.WithAllowedIPs("192.168.1.0/24"),
	servex.WithFilterStatusCode(403),
)

// Use 404 to hide the existence of endpoints
server := servex.New(
	servex.WithBlockedUserAgents("BadBot"),
	servex.WithFilterStatusCode(404),
)

// Use 429 to indicate rate limiting (misleading but sometimes useful)
server := servex.New(
	servex.WithBlockedIPs("malicious-range"),
	servex.WithFilterStatusCode(429),
)

Common status codes:

  • 403 Forbidden (recommended) - Clear about blocking
  • 404 Not Found - Hides endpoint existence
  • 401 Unauthorized - Suggests authentication needed
  • 429 Too Many Requests - Can mislead attackers

Choose based on your security strategy and user experience needs.

func WithFilterTrustedProxies

func WithFilterTrustedProxies(proxies ...string) Option

WithFilterTrustedProxies sets trusted proxy IP addresses or CIDR ranges for accurate client IP detection in filtering.

Example:

// Trust load balancer IPs for filtering
server := servex.New(
	servex.WithAllowedIPs("192.168.1.0/24"),
	servex.WithFilterTrustedProxies("10.0.0.0/8", "172.16.0.0/12"),
)

// Trust specific proxy servers
server := servex.New(
	servex.WithBlockedIPs("malicious-range"),
	servex.WithFilterTrustedProxies("192.168.1.100", "192.168.1.101"),
)

How it works:

  • Without trusted proxies: Uses r.RemoteAddr (proxy IP) for IP filtering
  • With trusted proxies: Uses X-Forwarded-For or X-Real-IP headers

Common proxy ranges:

  • AWS ALB: Check AWS documentation for current ranges
  • Cloudflare: Use Cloudflare's published IP ranges
  • Internal load balancers: Your internal network ranges
  • Docker networks: 172.16.0.0/12, 10.0.0.0/8

Security considerations:

  • Only list IPs you actually trust
  • Malicious clients can spoof X-Forwarded-For headers
  • Ensure proxy properly validates and forwards real client IPs
  • Consider using separate trusted proxy lists for different purposes

func WithHSTSHeader

func WithHSTSHeader(maxAge int, includeSubdomains, preload bool) Option

WithHSTSHeader sets the Strict-Transport-Security header to enforce HTTPS connections. HSTS prevents protocol downgrade attacks and cookie hijacking.

Parameters:

  • maxAge: Maximum age in seconds (typically 31536000 for 1 year)
  • includeSubdomains: Whether to apply to all subdomains
  • preload: Whether to include in browser HSTS preload lists

Example:

// Basic HSTS for 1 year
server := servex.New(servex.WithHSTSHeader(31536000, false, false))
// Header: Strict-Transport-Security: max-age=31536000

// HSTS with subdomains for 1 year
server := servex.New(servex.WithHSTSHeader(31536000, true, false))
// Header: Strict-Transport-Security: max-age=31536000; includeSubDomains

// Full HSTS with preload
server := servex.New(servex.WithHSTSHeader(63072000, true, true))
// Header: Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

Recommended values:

  • Development: 300 (5 minutes) or 0 to disable
  • Staging: 86400 (1 day)
  • Production: 31536000 (1 year) or more

Important considerations:

  • Only enable HSTS when you're confident HTTPS works correctly
  • Once enabled, browsers will refuse HTTP connections for the duration
  • Preload requires HTTPS to be working perfectly
  • Use short max-age initially, increase gradually

Warning: Incorrect HSTS configuration can make your site inaccessible. Test thoroughly before using long max-age values or preload.

func WithHTTPSRedirect added in v2.1.0

func WithHTTPSRedirect() Option

WithHTTPSRedirect configures the server to automatically redirect HTTP requests to HTTPS. This is a convenience method for the most common HTTPS redirect scenario.

func WithHTTPSRedirectConfig added in v2.1.0

func WithHTTPSRedirectConfig(config HTTPSRedirectConfig) Option

WithHTTPSRedirectConfig sets the complete HTTPS redirection configuration. This allows fine-grained control over all HTTPS redirect settings.

Example:

httpsConfig := servex.HTTPSRedirectConfig{
	Enabled: true,
	Permanent: true,
	TrustedProxies: []string{"10.0.0.0/8", "172.16.0.0/12"},
	ExcludePaths: []string{"/health", "/.well-known/*"},
}

server := servex.New(servex.WithHTTPSRedirectConfig(httpsConfig))

Use this when you need to configure multiple HTTPS redirect settings at once or when loading configuration from files or environment variables.

func WithHTTPSRedirectExcludePaths added in v2.1.0

func WithHTTPSRedirectExcludePaths(paths ...string) Option

WithHTTPSRedirectExcludePaths sets paths that should be excluded from HTTPS redirection. Requests to these paths will not be redirected to HTTPS.

Common exclusions:

  • Health checks: "/health", "/ping" (for load balancers that use HTTP)
  • Let's Encrypt challenges: "/.well-known/acme-challenge/*"
  • Development endpoints: "/debug/*"
  • Legacy integrations that require HTTP: "/legacy/*"

Example:

// Exclude health checks and Let's Encrypt challenges
server := servex.New(
	servex.WithHTTPSRedirect(),
	servex.WithHTTPSRedirectExcludePaths("/health", "/.well-known/*"),
)

Path matching supports wildcards (*) for pattern matching. Use sparingly - most paths should use HTTPS for security.

func WithHTTPSRedirectIncludePaths added in v2.1.0

func WithHTTPSRedirectIncludePaths(paths ...string) Option

WithHTTPSRedirectIncludePaths sets paths that should be included in HTTPS redirection. If set, only requests to these paths will be redirected to HTTPS.

If both IncludePaths and ExcludePaths are set:

  1. Paths must match IncludePaths to be considered for redirection
  2. Paths in ExcludePaths are then excluded from redirection

Example:

// Only redirect specific sensitive areas
server := servex.New(
	servex.WithHTTPSRedirect(),
	servex.WithHTTPSRedirectIncludePaths("/admin/*", "/user/*", "/api/private/*"),
)

// Gradual HTTPS migration
server := servex.New(
	servex.WithHTTPSRedirect(),
	servex.WithHTTPSRedirectIncludePaths("/secure/*"),
)

Use cases:

  • Gradual HTTPS migration: Start with specific paths
  • Mixed HTTP/HTTPS applications: Only secure sensitive areas
  • Testing HTTPS setup: Limit scope during testing

Path matching supports wildcards (*) for pattern matching. Leave empty to redirect all paths (recommended for production).

func WithHTTPSRedirectTemporary added in v2.1.0

func WithHTTPSRedirectTemporary() Option

WithHTTPSRedirectTemporary enables automatic HTTP to HTTPS redirection using temporary redirects (302). This is useful during development and testing when you don't want browsers to cache the redirects.

Example:

// Enable temporary HTTPS redirection for development
server := servex.New(servex.WithHTTPSRedirectTemporary())

For production use, prefer WithHTTPSRedirect() which uses permanent redirects (301) for better SEO and performance.

func WithHTTPSRedirectTrustedProxies added in v2.1.0

func WithHTTPSRedirectTrustedProxies(proxies ...string) Option

WithHTTPSRedirectTrustedProxies sets the trusted proxy IP addresses or CIDR ranges for accurate HTTP/HTTPS detection when behind load balancers or proxies.

When behind proxies, the server cannot detect HTTPS from r.TLS alone. This setting allows checking X-Forwarded-Proto and similar headers only when the request comes from trusted proxy IPs.

Example:

// Trust common internal networks
server := servex.New(
	servex.WithHTTPSRedirect(),
	servex.WithHTTPSRedirectTrustedProxies("10.0.0.0/8", "172.16.0.0/12"),
)

// Trust specific load balancer IPs
server := servex.New(
	servex.WithHTTPSRedirect(),
	servex.WithHTTPSRedirectTrustedProxies("192.168.1.100", "192.168.1.101"),
)

Security note: Only list IPs you actually trust. Malicious clients can spoof X-Forwarded-Proto headers if the proxy IP is trusted.

func WithHealthEndpoint

func WithHealthEndpoint() Option

WithHealthEndpoint enables an automatic health check endpoint that returns server status. This creates a simple endpoint that responds with "OK" and HTTP 200 status.

Note: The health endpoint is enabled by default. You only need to use this function if you want to explicitly enable it after disabling it, or to ensure it's enabled for clarity in your code.

Example:

// Health endpoint is already enabled by default
server := servex.New()
// Available at: GET /health

// Explicitly enable (redundant but clear)
server := servex.New(servex.WithHealthEndpoint())
// Available at: GET /health

// Custom health path
server := servex.New(
	servex.WithHealthEndpoint(),
	servex.WithHealthPath("/status"),
)
// Available at: GET /status

The health endpoint:

  • Returns 200 OK with "OK" body when server is running
  • Bypasses authentication and filtering
  • Suitable for load balancer health checks
  • Kubernetes liveness/readiness probes
  • Monitoring systems

Use cases:

  • Load balancer health checks
  • Kubernetes probes
  • Monitoring and alerting
  • Service discovery
  • Uptime monitoring

To disable the health endpoint, use WithDisableHealthEndpoint(). For custom health logic, implement your own endpoint instead of using this option.

func WithHealthPath

func WithHealthPath(path string) Option

WithHealthPath sets a custom path for the health check endpoint. This only works when WithHealthEndpoint() is also used.

Example:

// Custom health check path
server := servex.New(
	servex.WithHealthEndpoint(),
	servex.WithHealthPath("/ping"),
)
// Available at: GET /ping

// Health check for specific service
server := servex.New(
	servex.WithHealthEndpoint(),
	servex.WithHealthPath("/api/v1/health"),
)
// Available at: GET /api/v1/health

Common health check paths:

  • "/health" (default)
  • "/ping"
  • "/status"
  • "/healthz" (Kubernetes style)
  • "/alive"
  • "/ready"

Default is "/health" if not specified. The path should start with "/" and be unique to avoid conflicts.

func WithIdleTimeout

func WithIdleTimeout(tm time.Duration) Option

WithIdleTimeout sets the maximum duration that idle Keep-Alive connections will be kept open. After this timeout, idle connections are closed.

A zero or negative value sets the default of 180 seconds.

Example:

// Short idle timeout for high-throughput servers
server := servex.New(servex.WithIdleTimeout(30 * time.Second))

// Longer timeout for persistent connections
server := servex.New(servex.WithIdleTimeout(5 * time.Minute))

Recommended values:

  • Web applications: 120-180 seconds
  • APIs with frequent requests: 60-120 seconds
  • Microservices: 30-60 seconds
  • WebSocket services: 300+ seconds

Shorter timeouts reduce resource usage but may impact performance for clients making frequent requests. Longer timeouts improve performance but consume more server resources.

func WithLogFields

func WithLogFields(fields ...string) Option

WithLogFields specifies which fields to include in request logs. If not set, all available fields will be logged (default behavior).

Example:

// Log only essential fields
server := servex.New(servex.WithLogFields(
	servex.MethodLogField,
	servex.URLLogField,
	servex.StatusLogField,
	servex.DurationLogField,
))

// Log minimal fields for privacy compliance
server := servex.New(servex.WithLogFields(
	servex.MethodLogField,
	servex.StatusLogField,
	servex.DurationLogField,
))

Available fields:

  • RequestIDLogField: Request ID
  • IPLogField: Client IP address
  • UserAgentLogField: User-Agent header
  • URLLogField: Request URL
  • MethodLogField: HTTP method (GET, POST, etc.)
  • ProtoLogField: HTTP protocol version
  • ErrorLogField: Error information
  • ErrorMessageLogField: Error message
  • StatusLogField: HTTP status code
  • DurationLogField: Request duration in milliseconds

Use this to:

  • Reduce log verbosity and storage costs
  • Focus on specific metrics or debugging needs
  • Comply with privacy regulations (e.g., exclude IP addresses)
  • Optimize performance by logging fewer fields

Note: This only affects the default BaseRequestLogger. Custom RequestLogger implementations are not affected by this setting.

func WithLogger

func WithLogger(l Logger) Option

WithLogger sets a custom logger for server events, errors, and panics. The logger must implement the Logger interface. Set via WithLogger().

If not set, servex will create a JSON logger that writes to stderr.

The logger receives:

  • Server startup/shutdown events (Info level)
  • Request errors and panics (Error level)
  • Debug information when available (Debug level)

func WithMaxFileUploadSize

func WithMaxFileUploadSize(size int64) Option

WithMaxFileUploadSize sets the maximum allowed file upload size in bytes. This applies to multipart form uploads and file uploads.

Common configurations:

  • Profile images: WithMaxFileUploadSize(10 << 20) // 10 MB
  • Document uploads: WithMaxFileUploadSize(200 << 20) // 200 MB
  • Media files: WithMaxFileUploadSize(2 << 30) // 2 GB
  • Data imports: WithMaxFileUploadSize(1 << 30) // 1 GB

Consider your server's available memory and disk space when setting this limit.

func WithMaxHeaderBytes added in v2.3.0

func WithMaxHeaderBytes(size int) Option

WithMaxHeaderBytes sets the maximum size of request headers the server will accept. This controls the maximum number of bytes the server will read parsing the request header's keys and values, including the request line. It does not limit the size of the request body.

Example:

// Set max header size to 2 MB
server := servex.New(servex.WithMaxHeaderBytes(2 << 20))

// Use with other timeouts for comprehensive protection
server := servex.New(
	servex.WithMaxHeaderBytes(1 << 20),    // 1 MB max headers
	servex.WithReadHeaderTimeout(10 * time.Second),
	servex.WithReadTimeout(30 * time.Second),
)

Recommended values:

  • Most applications: 1 MB (1 << 20)
  • API servers: 512 KB - 1 MB
  • Applications with large headers: 2-4 MB
  • Restrictive applications: 256 KB

Default: 1 MB (1 << 20) if not set or zero.

Setting this helps prevent attacks where clients send extremely large headers to consume server resources. A reasonable limit protects against slowloris-style attacks.

func WithMaxJSONBodySize

func WithMaxJSONBodySize(size int64) Option

WithMaxJSONBodySize sets the maximum allowed JSON request body size in bytes. This specifically applies to JSON payloads and takes precedence over MaxRequestBodySize for JSON.

Recommended values:

  • API servers: WithMaxJSONBodySize(5 << 20) // 5 MB
  • Configuration APIs: WithMaxJSONBodySize(1 << 20) // 1 MB
  • Data import APIs: WithMaxJSONBodySize(50 << 20) // 50 MB
  • Real-time APIs: WithMaxJSONBodySize(1 << 20) // 1 MB

Smaller JSON limits help prevent JSON parsing attacks and reduce memory usage.

func WithMaxMultipartMemory

func WithMaxMultipartMemory(size int64) Option

WithMaxMultipartMemory sets the maximum memory used for multipart form parsing in bytes. Files larger than this are stored in temporary files on disk.

Balance considerations:

  • Higher values: Faster processing, more memory usage
  • Lower values: Slower processing, less memory usage, more disk I/O

Recommended: 10-50 MB for most applications Example: WithMaxMultipartMemory(32 << 20) // 32 MB

func WithMaxRequestBodySize

func WithMaxRequestBodySize(size int64) Option

WithMaxRequestBodySize sets the maximum allowed request body size in bytes. This applies to all request bodies including JSON, form data, and file uploads. Use 0 to disable global request size limits.

Common configurations:

  • API servers: WithMaxRequestBodySize(10 << 20) // 10 MB
  • Web applications: WithMaxRequestBodySize(50 << 20) // 50 MB
  • File upload services: WithMaxRequestBodySize(1 << 30) // 1 GB
  • Microservices: WithMaxRequestBodySize(5 << 20) // 5 MB

This is a global limit applied via middleware. Individual endpoints can use smaller limits via context methods like ReadJSONWithLimit().

func WithMetrics

func WithMetrics(m Metrics) Option

WithMetrics sets a custom metrics collector that will be called on each HTTP request and response.

The Metrics interface requires two methods:

  • HandleRequest(r *http.Request) - Called when a request arrives
  • HandleResponse(r *http.Request, w http.ResponseWriter, statusCode int, duration time.Duration) - Called when response is sent

This allows you to collect detailed metrics about your application's HTTP traffic. When using a custom metrics implementation, the default metrics endpoint is automatically disabled.

Example - Basic Request Counter:

type MyMetrics struct {
	requestCount int64
}

func (m *MyMetrics) HandleRequest(r *http.Request) {
	atomic.AddInt64(&m.requestCount, 1)
}

func (m *MyMetrics) HandleResponse(r *http.Request, w http.ResponseWriter, statusCode int, duration time.Duration) {
	// Track response metrics, status codes, response times, etc.
}

metrics := &MyMetrics{}
server := servex.New(servex.WithMetrics(metrics))

Example - Prometheus Integration:

type PrometheusMetrics struct {
	requestsTotal   *prometheus.CounterVec
	requestDuration *prometheus.HistogramVec
}

func (m *PrometheusMetrics) HandleRequest(r *http.Request) {
	// Increment request counter
	m.requestsTotal.WithLabelValues(r.Method, r.URL.Path).Inc()
}

func (m *PrometheusMetrics) HandleResponse(r *http.Request, w http.ResponseWriter, statusCode int, duration time.Duration) {
	// Record response time and status
	m.requestDuration.WithLabelValues(r.Method, r.URL.Path, fmt.Sprint(statusCode)).Observe(duration.Seconds())
}

Use cases:

  • Prometheus metrics collection with custom labels and buckets
  • StatsD or DataDog integration
  • Custom analytics and monitoring systems
  • Application Performance Monitoring (APM) integration
  • Request/response tracking for debugging
  • Business metrics (e.g., tracking API usage per customer)

Performance considerations:

  • The metrics methods are called for EVERY request, ensure they are fast and non-blocking
  • Avoid expensive operations like database writes or external API calls
  • Use atomic operations for counters to avoid mutex contention
  • Consider using buffering or background workers for complex metric processing

Note: Using WithMetrics disables the default metrics endpoint. If you need both built-in and custom metrics, consider wrapping or composing metrics implementations, or use WithDefaultMetrics() instead for the built-in Prometheus-compatible metrics endpoint.

func WithMetricsAndDefault added in v2.3.0

func WithMetricsAndDefault(customMetrics Metrics, path ...string) Option

WithMetricsAndDefault enables both custom metrics and the default built-in metrics simultaneously.

This function combines your custom Metrics implementation with the built-in default metrics, calling both on each request. This is useful when you want to:

  • Send metrics to your own monitoring system (e.g., custom Prometheus metrics)
  • Keep the built-in /metrics endpoint for quick observability
  • Have both detailed custom metrics and standard HTTP metrics

Both metrics implementations will receive HandleRequest and HandleResponse calls for every request. The default metrics endpoint will still be available at the specified path (default: "/metrics").

Example:

// Your custom Prometheus metrics
customMetrics := &MyPrometheusMetrics{
	requestsTotal: prometheus.NewCounterVec(...),
	// ... other custom metrics
}

// Enable both custom and default metrics
server := servex.New(
	servex.WithMetricsAndDefault(customMetrics),
)
// Now both your custom metrics AND built-in metrics are active
// The /metrics endpoint shows the built-in metrics

// Custom path for built-in metrics
server := servex.New(
	servex.WithMetricsAndDefault(customMetrics, "/stats"),
)

Use cases:

  • Gradual migration from built-in to custom metrics
  • Running both systems in parallel for comparison
  • Custom business metrics alongside standard HTTP metrics
  • Different metrics for different monitoring systems

Performance note: Both metrics implementations will be called on every request. Ensure both are optimized for high-frequency calls.

func WithMetricsDefault added in v2.3.0

func WithMetricsDefault(path ...string) Option

WithMetricsDefault is an alias for WithDefaultMetrics. It enables the default built-in metrics endpoint with Prometheus-compatible output.

This function provides an alternative naming convention that some developers may find more intuitive (WithMetricsDefault vs WithDefaultMetrics).

Example:

// These are equivalent:
server := servex.New(servex.WithDefaultMetrics())
server := servex.New(servex.WithMetricsDefault())

// Both enable the /metrics endpoint
server := servex.New(servex.WithMetricsDefault("/stats"))

See WithDefaultMetrics() for full documentation and available metrics.

func WithNoLogClientErrors

func WithNoLogClientErrors() Option

WithNoLogClientErrors disables logging of client errors in error level (HTTP status codes 400-499). Server errors (5xx) will still be logged in error level if request logging is enabled.

Example:

// Don't log 404s, 400s, etc. in error level to reduce noise
server := servex.New(servex.WithNoLogClientErrors())

Use this to:

  • Reduce log noise from bad requests
  • Focus on server-side issues
  • Improve log readability in production

Commonly filtered errors include:

  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 429 Too Many Requests

func WithNoRequestLog

func WithNoRequestLog() Option

WithNoRequestLog disables HTTP request logging completely. No requests will be logged regardless of status or errors.

Example:

// Disable all request logging
server := servex.New(servex.WithNoRequestLog())

Use this when:

  • You have external request logging (load balancer, proxy)
  • You want to reduce log volume
  • Performance is critical and logging overhead matters
  • You're implementing custom request logging middleware

Note: This only disables request logging. Server events, errors, and panics will still be logged through the main logger.

func WithProxyConfig

func WithProxyConfig(proxy ProxyConfiguration) Option

WithProxyConfig sets the reverse proxy configuration. This enables L7 reverse proxy/API gateway functionality with load balancing, traffic dumping, health checking, and advanced routing capabilities.

Example:

proxyConfig := ProxyConfiguration{
  Enabled: true,
  Rules: []ProxyRule{
    {
      Name: "api-backend",
      PathPrefix: "/api/",
      Backends: []Backend{
        {URL: "http://backend1:8080", Weight: 2},
        {URL: "http://backend2:8080", Weight: 1},
      },
      LoadBalancing: WeightedRoundRobinStrategy,
      StripPrefix: "/api",
    },
  },
  TrafficDump: TrafficDumpConfig{
    Enabled: true,
    Directory: "./traffic_dumps",
    IncludeBody: true,
  },
}
server, _ := servex.New(servex.WithProxyConfig(proxyConfig))

Features:

  • Multiple load balancing strategies (round-robin, weighted, least-connections, etc.)
  • Health checking with automatic backend failover
  • Traffic dumping for analysis and debugging
  • Path-based and header-based routing
  • Connection pooling and timeout management
  • RAW HTTP request logging

func WithRPM

func WithRPM(rpm int) Option

WithRPM sets rate limiting to allow a specific number of requests per minute. This is a convenience function for simple rate limiting configuration.

Example:

// Allow 1000 requests per minute per client
server := servex.New(servex.WithRPM(1000))

// Strict rate limiting for public APIs
server := servex.New(servex.WithRPM(60)) // 1 request per second average

Equivalent to:

servex.WithRequestsPerInterval(rpm, time.Minute)

Common RPM values:

  • Public APIs: 60-1000 RPM
  • Internal APIs: 1000-10000 RPM
  • File uploads: 10-100 RPM
  • Authentication: 10-60 RPM

func WithRPS

func WithRPS(rps int) Option

WithRPS sets rate limiting to allow a specific number of requests per second. This is a convenience function for simple rate limiting configuration.

Example:

// Allow 10 requests per second per client
server := servex.New(servex.WithRPS(10))

// High-throughput API
server := servex.New(servex.WithRPS(100))

Equivalent to:

servex.WithRequestsPerInterval(rps, time.Second)

Common RPS values:

  • Web applications: 1-10 RPS
  • APIs: 10-100 RPS
  • High-performance APIs: 100-1000 RPS
  • Microservices: 50-500 RPS

func WithRateLimitConfig

func WithRateLimitConfig(rateLimit RateLimitConfig) Option

WithRateLimitConfig sets the complete rate limiting configuration at once. This allows fine-grained control over all rate limiting settings.

Example:

rateLimitConfig := servex.RateLimitConfig{
	Enabled:             true,
	RequestsPerInterval: 100,
	Interval:            time.Minute,
	BurstSize:           20,
	StatusCode:          429,
	Message:             "Rate limit exceeded. Try again later.",
	ExcludePaths:        []string{"/health", "/metrics"},
	TrustedProxies:      []string{"10.0.0.0/8"},
}

server := servex.New(servex.WithRateLimitConfig(rateLimitConfig))

Use this when you need to configure multiple rate limiting settings at once or when loading configuration from files or environment variables.

func WithRateLimitExcludePaths

func WithRateLimitExcludePaths(paths ...string) Option

WithRateLimitExcludePaths excludes specific paths from rate limiting. Requests to these paths will not be counted against rate limits.

Example:

// Exclude monitoring endpoints
server := servex.New(
	servex.WithRPS(10),
	servex.WithRateLimitExcludePaths("/health", "/metrics", "/status"),
)

// Exclude static assets
server := servex.New(
	servex.WithRPS(100),
	servex.WithRateLimitExcludePaths("/static/*", "/assets/*", "/favicon.ico"),
)

Common exclusions:

  • Health checks: "/health", "/ping"
  • Metrics: "/metrics", "/stats"
  • Static files: "/static/*", "/assets/*"
  • Documentation: "/docs/*", "/swagger/*"
  • Infrastructure: "/robots.txt", "/favicon.ico"

Path matching supports wildcards (*) for pattern matching.

func WithRateLimitIncludePaths

func WithRateLimitIncludePaths(paths ...string) Option

WithRateLimitIncludePaths specifies which paths should be rate limited. If set, only requests to these paths will be rate limited. All other paths are excluded.

Example:

// Only rate limit API endpoints
server := servex.New(
	servex.WithRPS(100),
	servex.WithRateLimitIncludePaths("/api/*"),
)

// Rate limit specific sensitive endpoints
server := servex.New(
	servex.WithRPS(5),
	servex.WithRateLimitIncludePaths("/api/auth/*", "/api/admin/*"),
)

If both IncludePaths and ExcludePaths are set:

  1. Paths must match IncludePaths to be rate limited
  2. Paths in ExcludePaths are then excluded from rate limiting

Use cases:

  • Protect only sensitive endpoints
  • Apply different limits to different API versions
  • Rate limit only external-facing endpoints
  • Granular control over protection

func WithRateLimitKeyFunc

func WithRateLimitKeyFunc(keyFunc func(r *http.Request) string) Option

WithRateLimitKeyFunc sets a custom function to extract the rate limit key from requests. This determines how clients are identified for rate limiting purposes.

Example:

// Rate limit by IP address
server := servex.New(
	servex.WithRPS(10),
	servex.WithRateLimitKeyFunc(func(r *http.Request) string {
		return r.RemoteAddr
	}),
)

// Rate limit by API key
server := servex.New(
	servex.WithRPS(100),
	servex.WithRateLimitKeyFunc(func(r *http.Request) string {
		apiKey := r.Header.Get("X-API-Key")
		if apiKey == "" {
			return r.RemoteAddr // Fallback to IP
		}
		return "api:" + apiKey
	}),
)

// Rate limit by user ID (requires auth)
server := servex.New(
	servex.WithRPS(50),
	servex.WithRateLimitKeyFunc(func(r *http.Request) string {
		userID := r.Context().Value("userID")
		if userID != nil {
			return "user:" + userID.(string)
		}
		return r.RemoteAddr
	}),
)

Default behavior uses client IP address. Custom key functions enable:

  • User-based rate limiting
  • API key-based limits
  • Different limits for different client types
  • Combined identification strategies

func WithRateLimitMessage

func WithRateLimitMessage(message string) Option

WithRateLimitMessage sets the response message when rate limit is exceeded. Default is "rate limit exceeded, try again later." if not set.

Example:

// Custom rate limit message
server := servex.New(
	servex.WithRPS(10),
	servex.WithRateLimitMessage("Too many requests. Please slow down and try again in a few moments."),
)

// Include retry information
server := servex.New(
	servex.WithRPM(100),
	servex.WithRateLimitMessage("Rate limit exceeded. Maximum 100 requests per minute allowed."),
)

Best practices:

  • Be clear about the limit
  • Suggest when to retry
  • Keep messages user-friendly
  • Include contact information for questions

The message is returned as plain text in the response body.

func WithRateLimitStatusCode

func WithRateLimitStatusCode(statusCode int) Option

WithRateLimitStatusCode sets the HTTP status code returned when rate limit is exceeded. Default is 429 (Too Many Requests) if not set.

Example:

// Use standard 429 status
server := servex.New(
	servex.WithRPS(10),
	servex.WithRateLimitStatusCode(429),
)

// Use 503 Service Unavailable
server := servex.New(
	servex.WithRPS(10),
	servex.WithRateLimitStatusCode(503),
)

Common status codes:

  • 429 Too Many Requests (recommended)
  • 503 Service Unavailable
  • 502 Bad Gateway (for proxy scenarios)

The 429 status code is specifically designed for rate limiting and is understood by most HTTP clients and libraries.

func WithRateLimitTrustedProxies

func WithRateLimitTrustedProxies(proxies ...string) Option

WithRateLimitTrustedProxies sets trusted proxy IP addresses or CIDR ranges for accurate client IP detection in rate limiting.

Example:

// Trust load balancer IPs
server := servex.New(
	servex.WithRPS(10),
	servex.WithRateLimitTrustedProxies("10.0.0.0/8", "172.16.0.0/12"),
)

// Trust specific proxy servers
server := servex.New(
	servex.WithRPS(100),
	servex.WithRateLimitTrustedProxies("192.168.1.100", "192.168.1.101"),
)

How it works:

  • Without trusted proxies: Uses r.RemoteAddr (proxy IP)
  • With trusted proxies: Uses X-Forwarded-For or X-Real-IP headers

Common proxy ranges:

  • AWS ALB: Check AWS documentation for current ranges
  • Cloudflare: Use Cloudflare's published IP ranges
  • Internal load balancers: Your internal network ranges
  • Docker networks: 172.16.0.0/12, 10.0.0.0/8

Security note: Only list IPs you actually trust. Malicious clients can spoof X-Forwarded-For headers if the proxy IP is trusted.

func WithReadHeaderTimeout

func WithReadHeaderTimeout(tm time.Duration) Option

WithReadHeaderTimeout sets the maximum duration for reading request headers. This timeout is specifically for reading the HTTP headers, not the body. After headers are read, ReadTimeout takes over for the body.

A zero or negative value sets the default of 60 seconds.

Example:

// Fast header timeout for performance
server := servex.New(servex.WithReadHeaderTimeout(5 * time.Second))

// Combined with read timeout
server := servex.New(
	servex.WithReadHeaderTimeout(5 * time.Second),
	servex.WithReadTimeout(30 * time.Second),
)

Recommended values:

  • Most applications: 2-10 seconds
  • High-performance APIs: 2-5 seconds
  • Development: 10-30 seconds

This should typically be shorter than ReadTimeout since headers are usually small. Protects against slow header attacks where clients send headers very slowly.

func WithReadTimeout

func WithReadTimeout(tm time.Duration) Option

WithReadTimeout sets the maximum duration for reading the entire request, including the body. This timeout starts when the connection is accepted and ends when the request body is fully read. It includes time for reading headers and body.

A zero or negative value sets the default of 60 seconds.

Example:

// Short timeout for API servers
server := servex.New(servex.WithReadTimeout(10 * time.Second))

// Longer timeout for file upload endpoints
server := servex.New(servex.WithReadTimeout(5 * time.Minute))

Recommended values:

  • API servers: 10-30 seconds
  • Web applications: 30-60 seconds
  • File upload services: 5-15 minutes
  • Microservices: 5-15 seconds

Setting this too low may cause legitimate requests to timeout. Setting this too high may allow slow clients to exhaust server resources.

func WithRemoveHeaders

func WithRemoveHeaders(headers ...string) Option

WithRemoveHeaders removes specific headers from responses. This is useful for removing server identification headers or other unwanted headers.

Example:

// Remove server identification headers
server := servex.New(servex.WithRemoveHeaders("Server", "X-Powered-By"))

// Remove additional headers for security
server := servex.New(servex.WithRemoveHeaders(
	"Server",
	"X-Powered-By",
	"X-AspNet-Version",
	"X-AspNetMvc-Version",
))

// Remove caching headers
server := servex.New(servex.WithRemoveHeaders("ETag", "Last-Modified"))

Common headers to remove:

  • "Server": Web server software identification
  • "X-Powered-By": Technology stack identification
  • "X-AspNet-Version": ASP.NET version (if proxying)
  • "X-AspNetMvc-Version": ASP.NET MVC version
  • "X-Generator": Content generator identification

Use cases:

  • Security through obscurity
  • Reduce information disclosure
  • Clean up response headers
  • Remove redundant headers
  • Compliance requirements

Note: This removes headers that might be added by the Go HTTP server, middleware, or upstream proxies. Some headers like "Server" are added by the Go standard library and will be removed by this option.

func WithRequestLogger

func WithRequestLogger(r RequestLogger) Option

WithRequestLogger sets a custom logger specifically for HTTP request logging. This is separate from the main logger and focuses on request/response details.

If not set, it will use the main Logger in debug level for successful requests.

Use for:

  • Structured request logging
  • Access logs
  • Request metrics
  • Audit trails

func WithRequestSizeLimits

func WithRequestSizeLimits() Option

WithRequestSizeLimits configures comprehensive request size limits with commonly used defaults. This is a convenience function that sets up reasonable defaults for most applications.

Default limits set:

  • MaxRequestBodySize: 100 MB
  • MaxJSONBodySize: 1 MB
  • MaxFileUploadSize: 100 MB
  • MaxMultipartMemory: 10 MB
  • EnableRequestSizeLimits: true

Use individual WithMax* functions for custom limits.

func WithRequestsPerInterval

func WithRequestsPerInterval(requestsPerInterval int, interval time.Duration) Option

WithRequestsPerInterval sets custom rate limiting with a specific number of requests allowed per time interval. This provides maximum flexibility for rate limiting configuration.

Example:

// 500 requests per 5 minutes
server := servex.New(servex.WithRequestsPerInterval(500, 5*time.Minute))

// 50 requests per 30 seconds
server := servex.New(servex.WithRequestsPerInterval(50, 30*time.Second))

// 1000 requests per hour
server := servex.New(servex.WithRequestsPerInterval(1000, time.Hour))

Use cases:

  • Custom business requirements
  • Unusual time windows
  • Integration with external rate limits
  • Compliance with API provider limits

The rate limiter uses a token bucket algorithm, refilling tokens at a constant rate.

func WithSPAMode

func WithSPAMode(dir, indexFile string) Option

WithSPAMode enables Single Page Application (SPA) mode for serving React, Vue, Angular apps. This serves static files from the directory and provides fallback routing for client-side navigation.

In SPA mode:

  • Static files are served normally (JS, CSS, images, etc.)
  • API routes continue to work (register them before calling this)
  • All other routes serve the index file for client-side routing

Parameters:

  • dir: Directory containing SPA build files (e.g., "build", "dist")
  • indexFile: Fallback file for client-side routing (typically "index.html")

Usage pattern:

  1. Register your API routes first
  2. Enable SPA mode last

Examples:

// React app setup
server, _ := servex.New(servex.WithSPAMode("build", "index.html"))
server.GET("/api/users", handleUsers)      // API routes work
server.GET("/about", handleUsers)          // Serves index.html for client routing

// Vue app setup
server, _ := servex.New(servex.WithSPAMode("dist", "index.html"))

func WithSecurityConfig

func WithSecurityConfig(security SecurityConfig) Option

WithSecurityConfig sets the complete security headers configuration at once. This allows fine-grained control over all security headers applied to responses.

Example:

securityConfig := servex.SecurityConfig{
	Enabled: true,
	ContentSecurityPolicy: "default-src 'self'; script-src 'self' 'unsafe-inline'",
	XContentTypeOptions: "nosniff",
	XFrameOptions: "DENY",
	XXSSProtection: "1; mode=block",
	StrictTransportSecurity: "max-age=31536000; includeSubDomains",
}

server := servex.New(servex.WithSecurityConfig(securityConfig))

Use this when you need to configure multiple security headers at once or when loading configuration from files or environment variables.

func WithSecurityExcludePaths

func WithSecurityExcludePaths(paths ...string) Option

WithSecurityExcludePaths excludes specific paths from security headers. Requests to these paths will not receive security headers.

Example:

// Exclude API endpoints from security headers
server := servex.New(
	servex.WithSecurityHeaders(),
	servex.WithSecurityExcludePaths("/api/*", "/webhooks/*"),
)

// Exclude development tools
server := servex.New(
	servex.WithStrictSecurityHeaders(),
	servex.WithSecurityExcludePaths("/debug/*", "/metrics", "/health"),
)

Common exclusions:

  • API endpoints: "/api/*" (may not need web security headers)
  • Webhooks: "/webhooks/*" (external services)
  • Health checks: "/health", "/ping"
  • Metrics: "/metrics", "/prometheus"
  • Development: "/debug/*", "/dev/*"
  • Static assets: "/static/*" (may need different CSP)

Use cases:

  • API endpoints that don't serve HTML
  • Third-party integrations
  • Resources with specific security requirements
  • Legacy endpoints with compatibility issues

Path matching supports wildcards (*) for pattern matching.

func WithSecurityHeaders

func WithSecurityHeaders() Option

WithSecurityHeaders enables basic security headers with safe default values. This is a convenience function that applies commonly recommended security headers.

Example:

// Apply basic security headers
server := servex.New(servex.WithSecurityHeaders())

Headers applied:

  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • X-XSS-Protection: 1; mode=block
  • Referrer-Policy: strict-origin-when-cross-origin

Use cases:

  • Quick security improvement
  • Development and testing
  • Basic web application protection
  • Starting point for custom security headers

For custom security headers or stricter settings, use WithStrictSecurityHeaders() or configure individual headers with specific options.

func WithSecurityIncludePaths

func WithSecurityIncludePaths(paths ...string) Option

WithSecurityIncludePaths specifies which paths should receive security headers. If set, only requests to these paths will get security headers applied.

Example:

// Only apply security headers to web pages
server := servex.New(
	servex.WithSecurityHeaders(),
	servex.WithSecurityIncludePaths("/", "/login", "/dashboard/*"),
)

// Apply to specific web applications
server := servex.New(
	servex.WithStrictSecurityHeaders(),
	servex.WithSecurityIncludePaths("/webapp/*", "/admin/*"),
)

If both IncludePaths and ExcludePaths are set:

  1. Paths must match IncludePaths to receive headers
  2. Paths in ExcludePaths are then excluded from headers

Use cases:

  • Mixed API and web application
  • Multiple applications on same server
  • Granular security control
  • Progressive security header rollout

Path matching supports wildcards (*) for pattern matching.

func WithSendErrorToClient

func WithSendErrorToClient() Option

WithSendErrorToClient configures the server to include detailed error information in HTTP responses when errors occur. This includes Go error messages and stack traces.

Example:

// Development server with detailed errors
server := servex.New(servex.WithSendErrorToClient())

// Production server (don't send error details)
server := servex.New() // Default is false

When enabled, responses might include:

  • Internal error messages
  • Stack traces for panics
  • Database connection errors
  • File system errors

Security considerations:

  • NEVER enable this in production
  • Error details can reveal system information
  • Use only for development and testing
  • Consider using structured error responses instead

For production, implement proper error handling that returns safe, user-friendly error messages while logging detailed errors server-side.

func WithStaticFileCache

func WithStaticFileCache(maxAge int, rules ...map[string]int) Option

WithStaticFileCache sets cache policies for static files. This controls how long browsers and proxies cache static files.

Parameters:

  • maxAge: Default cache duration in seconds
  • rules: File extension or path-specific cache rules

The rules map allows different cache durations for different file types:

  • Key: File extension (e.g., ".js", ".css") or path pattern (e.g., "/images/*")
  • Value: Cache duration in seconds

Example:

// Basic cache setup
servex.WithStaticFileCache(3600, nil) // 1 hour for all files

// Advanced cache setup with rules
rules := map[string]int{
	".js":        31536000, // 1 year for JS files
	".css":       31536000, // 1 year for CSS files
	".html":      3600,     // 1 hour for HTML files
	"/images/*":  2592000,  // 30 days for images
}
servex.WithStaticFileCache(86400, rules) // 1 day default, custom rules

func WithStaticFileConfig

func WithStaticFileConfig(config StaticFileConfig) Option

WithStaticFileConfig sets the static file serving configuration. This provides full control over all static file serving options. Use this when you need granular control over file serving behavior.

For simpler setups, consider using WithStaticFiles() or WithSPAMode() instead.

Example:

cfg := servex.StaticFileConfig{
	Enabled:      true,
	Dir:          "build",
	SPAMode:      true,
	IndexFile:    "index.html",
	CacheMaxAge:  3600,
	ExcludePaths: []string{"/api/*"},
	CacheRules: map[string]int{
		".js":  31536000, // 1 year
		".css": 31536000, // 1 year
		".html": 3600,    // 1 hour
	},
}
server, _ := servex.New(servex.WithStaticFileConfig(cfg))

func WithStaticFileExclusions

func WithStaticFileExclusions(paths ...string) Option

WithStaticFileExclusions sets paths that should not be served as static files. These paths will be skipped by the static file handler, allowing API routes to handle them.

This is useful when you want to exclude certain paths from static file serving, such as API endpoints that should be handled by custom handlers.

Parameters:

  • paths: List of path patterns to exclude (supports wildcards with *)

Common exclusions:

  • "/api/*": All API endpoints
  • "/auth/*": Authentication endpoints
  • "/admin/*": Admin interfaces
  • "/ws/*": WebSocket endpoints

Note: API routes registered before static files are automatically excluded.

Example:

server, _ := servex.New(
	servex.WithSPAMode("build", "index.html"),
	servex.WithStaticFileExclusions("/api/*", "/auth/*"),
)

func WithStaticFiles

func WithStaticFiles(dir, urlPrefix string) Option

WithStaticFiles enables static file serving from the specified directory. This is a simple way to serve static files from a directory.

Parameters:

  • dir: Directory containing static files to serve
  • urlPrefix: URL path prefix (empty for root, e.g., "/static")

For SPA support with client-side routing, use WithSPAMode() instead.

Examples:

// Serve files from "public/" at root path
servex.WithStaticFiles("public", "")

// Serve files from "assets/" under "/static" path
servex.WithStaticFiles("assets", "/static")

// Complete example
server, _ := servex.New(servex.WithStaticFiles("build", ""))

func WithStrictRequestSizeLimits

func WithStrictRequestSizeLimits() Option

WithStrictRequestSizeLimits enables request size limits with strict, secure default values. This configuration prioritizes security over convenience with smaller size limits. Use for applications where security is more important than convenience.

func WithStrictSecurityHeaders

func WithStrictSecurityHeaders() Option

WithStrictSecurityHeaders enables comprehensive security headers with strict settings. This applies a full set of security headers suitable for high-security environments.

Example:

// Apply strict security headers
server := servex.New(servex.WithStrictSecurityHeaders())

Headers applied:

  • Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'
  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • X-XSS-Protection: 1; mode=block
  • Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
  • Referrer-Policy: strict-origin-when-cross-origin
  • Permissions-Policy: camera=(), microphone=(), geolocation=()
  • X-Permitted-Cross-Domain-Policies: none
  • Cross-Origin-Embedder-Policy: require-corp
  • Cross-Origin-Opener-Policy: same-origin
  • Cross-Origin-Resource-Policy: same-site

Use cases:

  • High-security applications
  • Financial services
  • Healthcare applications
  • Government systems
  • Production web applications

Warning: These strict headers may break functionality that requires:

  • External scripts or stylesheets
  • Iframe embedding
  • Cross-origin requests
  • Third-party integrations

Test thoroughly and adjust headers as needed for your application.

type Options

type Options struct {
	// Certificate is the TLS certificate for HTTPS server support.
	// This enables HTTPS support when the server is started with an HTTPS address.
	// Use WithCertificate() to set a pre-loaded certificate, or WithCertificateFromFile()
	// to load from files. If not set, only HTTP will be available.
	//
	// Example:
	//   cert, _ := tls.LoadX509KeyPair("server.crt", "server.key")
	//   options.Certificate = &cert
	Certificate *tls.Certificate

	// CertFilePath is the path to the TLS certificate file for loading at server startup.
	// The file should contain the PEM-encoded certificate chain.
	// Used with KeyFilePath to enable HTTPS. Set via WithCertificateFromFile().
	//
	// Examples:
	//   - "/etc/ssl/certs/server.crt"
	//   - "./certs/certificate.pem"
	//   - "/path/to/fullchain.pem" (Let's Encrypt style)
	CertFilePath string

	// KeyFilePath is the path to the TLS private key file for loading at server startup.
	// The file should contain the PEM-encoded private key.
	// Used with CertFilePath to enable HTTPS. Set via WithCertificateFromFile().
	//
	// Examples:
	//   - "/etc/ssl/private/server.key"
	//   - "./certs/private.pem"
	//   - "/path/to/privkey.pem" (Let's Encrypt style)
	KeyFilePath string

	// ReadTimeout is the maximum duration for reading the entire request, including the body.
	// This timeout starts when the connection is accepted and ends when the request body
	// is fully read. Set via WithReadTimeout().
	//
	// Recommended values:
	//   - API servers: 10-30 seconds
	//   - Web applications: 30-60 seconds
	//   - File upload services: 5-15 minutes
	//   - Microservices: 5-15 seconds
	//
	// Default: 60 seconds if not set or zero.
	ReadTimeout time.Duration

	// ReadHeaderTimeout is the maximum duration for reading request headers.
	// This timeout is specifically for reading the HTTP headers, not the body.
	// After headers are read, ReadTimeout takes over for the body. Set via WithReadHeaderTimeout().
	//
	// Recommended values:
	//   - Most applications: 2-10 seconds
	//   - High-performance APIs: 2-5 seconds
	//   - Development: 10-30 seconds
	//
	// Default: 60 seconds if not set or zero.
	ReadHeaderTimeout time.Duration

	// IdleTimeout is the maximum duration that idle Keep-Alive connections will be kept open.
	// After this timeout, idle connections are closed. Set via WithIdleTimeout().
	//
	// Recommended values:
	//   - Web applications: 120-180 seconds
	//   - APIs with frequent requests: 60-120 seconds
	//   - Microservices: 30-60 seconds
	//   - WebSocket services: 300+ seconds
	//
	// Default: 180 seconds if not set or zero.
	IdleTimeout time.Duration

	// MaxHeaderBytes is the maximum size of request headers in bytes.
	// This controls the maximum number of bytes the server will read parsing the request header's keys and values,
	// including the request line. It does not limit the size of the request body.
	// Set via WithMaxHeaderBytes().
	//
	// Recommended values:
	//   - Most applications: 1 MB (1 << 20)
	//   - API servers: 512 KB - 1 MB
	//   - Applications with large headers: 2-4 MB
	//   - Restrictive applications: 256 KB
	//
	// Default: 1 MB (1 << 20) if not set or zero.
	//
	// Setting this helps prevent attacks where clients send extremely large headers
	// to consume server resources. A reasonable limit protects against slowloris-style attacks.
	MaxHeaderBytes int

	// AuthToken enables simple token-based authentication using the Authorization header.
	// When set, the server will check for "Authorization: Bearer <token>" headers on
	// protected routes. Set via WithAuthToken().
	//
	// Use cases:
	//   - Simple API authentication
	//   - Service-to-service communication
	//   - Development and testing
	//
	// For advanced authentication with user management, JWT tokens, and roles,
	// use the Auth field instead.
	AuthToken string

	// Logger is a custom logger for server events, errors, and panics.
	// The logger must implement the servex.Logger interface. Set via WithLogger().
	//
	// If not set, servex will create a JSON logger that writes to stderr.
	//
	// The logger receives:
	//   - Server startup/shutdown events (Info level)
	//   - Request errors and panics (Error level)
	//   - Debug information when available (Debug level)
	Logger Logger

	// RequestLogger is a custom logger specifically for HTTP request logging.
	// This is separate from the main logger and focuses on request/response details.
	// Set via WithRequestLogger().
	//
	// If not set, it will use the main Logger in debug level for successful requests.
	//
	// Use for:
	//   - Structured request logging
	//   - Access logs
	//   - Request metrics
	//   - Audit trails
	RequestLogger RequestLogger

	// DisableRequestLogging disables HTTP request logging completely.
	// No requests will be logged regardless of status or errors.
	// Set to true via WithNoRequestLog() or WithDisableRequestLogging().
	//
	// Use when:
	//   - You have external request logging (load balancer, proxy)
	//   - You want to reduce log volume
	//   - Performance is critical and logging overhead matters
	//   - You're implementing custom request logging middleware
	DisableRequestLogging bool

	// NoLogClientErrors disables logging of client errors in error level (HTTP status codes 400-499).
	// Server errors (5xx) will still be logged in error level if request logging is enabled.
	// Set to true via WithNoLogClientErrors().
	//
	// Use to:
	//   - Reduce log noise from bad requests
	//   - Focus on server-side issues
	//   - Improve log readability in production
	NoLogClientErrors bool

	// LogFields specifies which fields to include in request logs.
	// If empty, all available fields will be logged (default behavior).
	// Set via WithLogFields().
	//
	// Available fields (use exported constants):
	//   - servex.RequestIDLogField: Request ID
	//   - servex.IPLogField: Client IP address
	//   - servex.UserAgentLogField: User-Agent header
	//   - servex.URLLogField: Request URL
	//   - servex.MethodLogField: HTTP method (GET, POST, etc.)
	//   - servex.ProtoLogField: HTTP protocol version
	//   - servex.ErrorLogField: Error information
	//   - servex.ErrorMessageLogField: Error message
	//   - servex.StatusLogField: HTTP status code
	//   - servex.DurationLogField: Request duration in milliseconds
	//
	// Use to:
	//   - Reduce log verbosity
	//   - Focus on specific metrics
	//   - Comply with privacy requirements
	//   - Optimize log storage costs
	LogFields []string

	// SendErrorToClient configures the server to include detailed error information
	// in HTTP responses when errors occur. This includes Go error messages and stack traces.
	// Set to true via WithSendErrorToClient().
	//
	// Security considerations:
	//   - NEVER enable this in production
	//   - Error details can reveal system information
	//   - Use only for development and testing
	//
	// When enabled, responses might include:
	//   - Internal error messages
	//   - Stack traces for panics
	//   - Database connection errors
	//   - File system errors
	SendErrorToClient bool

	// Auth is the JWT-based authentication configuration with user management, roles, and JWT tokens.
	// Set via WithAuth(), WithAuthMemoryDatabase(), or WithAuthConfig().
	//
	// When configured, this automatically registers these endpoints:
	//   - POST /api/v1/auth/register - User registration
	//   - POST /api/v1/auth/login - User login
	//   - POST /api/v1/auth/refresh - Token refresh
	//   - POST /api/v1/auth/logout - User logout
	//   - GET /api/v1/auth/me - Current user info
	//
	// Use for:
	//   - Multi-user applications
	//   - Role-based access control
	//   - Persistent user data
	//   - Production authentication systems
	Auth AuthConfig

	// RateLimit is the rate limiting configuration to control request frequency per client.
	// Set via WithRateLimitConfig(), WithRPS(), WithRPM(), or other rate limiting options.
	//
	// If RequestsPerInterval is not set, rate limiting will be disabled.
	//
	// Common configurations:
	//   - Public APIs: 60-1000 RPM
	//   - Internal APIs: 1000-10000 RPM
	//   - File uploads: 10-100 RPM
	//   - Authentication: 10-60 RPM
	RateLimit RateLimitConfig

	// Filter is the request filtering configuration for IP addresses, User-Agents, headers, and query parameters.
	// Set via WithFilterConfig() or individual filter options like WithAllowedIPs(), WithBlockedUserAgents(), etc.
	//
	// Use for:
	//   - IP whitelisting/blacklisting
	//   - Bot protection
	//   - Geographic restrictions
	//   - Header-based filtering
	//   - Query parameter validation
	Filter FilterConfig

	// Security is the security headers configuration for web application protection.
	// Set via WithSecurityConfig(), WithSecurityHeaders(), WithStrictSecurityHeaders(), or individual header options.
	//
	// Common headers include:
	//   - Content-Security-Policy
	//   - X-Frame-Options
	//   - X-Content-Type-Options
	//   - Strict-Transport-Security
	//   - X-XSS-Protection
	//
	// Use for:
	//   - XSS protection
	//   - Clickjacking prevention
	//   - MIME type sniffing protection
	//   - HTTPS enforcement
	Security SecurityConfig

	// CustomHeaders are custom HTTP headers that will be added to all responses.
	// These headers are applied after security headers and can override them.
	// Set via WithCustomHeaders().
	//
	// Use for:
	//   - API versioning headers
	//   - Service identification
	//   - Custom caching policies
	//   - CORS configuration
	//   - Application-specific headers
	CustomHeaders map[string]string

	// AuditLogger is the security audit logger for logging security events.
	// Set via WithAuditLogger() or WithDefaultAuditLogger().
	//
	// Use for:
	//   - Security event logging and monitoring
	//   - Compliance requirements (SOX, GDPR, HIPAA)
	//   - Threat detection and analysis
	//   - Forensic investigation
	//   - Regulatory audit trails
	AuditLogger AuditLogger

	// EnableDefaultAuditLogger indicates that default audit logging was requested
	// even if the logger wasn't available when WithDefaultAuditLogger() was called
	EnableDefaultAuditLogger bool

	// HeadersToRemove specifies headers to remove from responses.
	// This is useful for removing server identification headers or other unwanted headers.
	// Set via WithRemoveHeaders().
	//
	// Common headers to remove:
	//   - "Server": Web server software identification
	//   - "X-Powered-By": Technology stack identification
	//   - "X-AspNet-Version": ASP.NET version (if proxying)
	//
	// Use for:
	//   - Security through obscurity
	//   - Reduce information disclosure
	//   - Clean up response headers
	HeadersToRemove []string

	// Cache is the cache control configuration for HTTP caching headers.
	// Set via WithCacheConfig(), WithCacheControl(), or other cache-related options.
	//
	// Controls browser and proxy caching behavior through standard HTTP headers:
	//   - Cache-Control: Main caching directive
	//   - Expires: Absolute expiration time
	//   - ETag: Entity tag for cache validation
	//   - Last-Modified: Resource modification time
	//   - Vary: Headers that affect caching
	//
	// Use for:
	//   - Performance optimization
	//   - Reduced server load
	//   - Improved user experience
	//   - CDN optimization
	Cache CacheConfig

	// StaticFiles is the static file serving configuration for serving web assets and SPAs.
	// Set via WithStaticFiles(), WithSPAMode(), or WithStaticFileConfig().
	//
	// Use for:
	//   - Serving React/Vue/Angular apps
	//   - Static asset serving (CSS, JS, images)
	//   - Single Page Application (SPA) routing
	//   - Progressive Web App (PWA) support
	//
	// Common use cases:
	//   - React app with API routes: Serve build/ folder with API at /api/*
	//   - Documentation site: Serve docs/ folder
	//   - Static website: Serve public/ folder
	//   - Mixed SPA + API: Client routing with server API endpoints
	StaticFiles StaticFileConfig

	// MaxRequestBodySize is the maximum allowed request body size in bytes.
	// This applies to all request bodies including JSON, form data, and file uploads.
	// Set via WithMaxRequestBodySize().
	//
	// Default values if not set:
	//   - 32 MB for general request bodies
	//   - Use 0 to disable global request size limits
	//
	// Common configurations:
	//   - API servers: 1-10 MB
	//   - Web applications: 10-50 MB
	//   - File upload services: 100 MB - 1 GB
	//   - Microservices: 1-5 MB
	//
	// This is a global limit applied via middleware. Individual endpoints
	// can use smaller limits via context methods like ReadJSONWithLimit().
	MaxRequestBodySize int64

	// MaxJSONBodySize is the maximum allowed JSON request body size in bytes.
	// This specifically applies to JSON payloads and takes precedence over MaxRequestBodySize for JSON.
	// Set via WithMaxJSONBodySize().
	//
	// Default: 1 MB if not set
	//
	// Recommended values:
	//   - API servers: 1-5 MB
	//   - Configuration APIs: 100 KB - 1 MB
	//   - Data import APIs: 5-50 MB
	//   - Real-time APIs: 100 KB - 1 MB
	//
	// Smaller JSON limits help prevent JSON parsing attacks and reduce memory usage.
	MaxJSONBodySize int64

	// MaxFileUploadSize is the maximum allowed file upload size in bytes.
	// This applies to multipart form uploads and file uploads.
	// Set via WithMaxFileUploadSize().
	//
	// Default: 100 MB if not set
	//
	// Common configurations:
	//   - Profile images: 5-10 MB
	//   - Document uploads: 50-200 MB
	//   - Media files: 500 MB - 2 GB
	//   - Data imports: 100 MB - 1 GB
	//
	// Consider your server's available memory and disk space when setting this limit.
	MaxFileUploadSize int64

	// MaxMultipartMemory is the maximum memory used for multipart form parsing in bytes.
	// Files larger than this are stored in temporary files on disk.
	// Set via WithMaxMultipartMemory().
	//
	// Default: 10 MB if not set
	//
	// Balance considerations:
	//   - Higher values: Faster processing, more memory usage
	//   - Lower values: Slower processing, less memory usage, more disk I/O
	//
	// Recommended: 10-50 MB for most applications
	MaxMultipartMemory int64

	// EnableRequestSizeLimits enables global request size limit middleware.
	// When enabled, all requests are checked against the configured size limits.
	// Set via WithEnableRequestSizeLimits() or WithRequestSizeLimits().
	//
	// When disabled, only individual endpoint size limits (via context methods) are enforced.
	//
	// Use cases for disabling:
	//   - Fine-grained control per endpoint
	//   - Custom size limit middleware
	//   - Performance-critical applications
	//   - Legacy compatibility
	EnableRequestSizeLimits bool

	// EnableHealthEndpoint enables an automatic health check endpoint that returns server status.
	// This creates a simple endpoint that responds with "OK" and HTTP 200 status.
	// Set to true via WithHealthEndpoint().
	//
	// The health endpoint:
	//   - Returns 200 OK with "OK" body when server is running
	//   - Bypasses authentication and filtering
	//   - Suitable for load balancer health checks
	//   - Kubernetes liveness/readiness probes
	EnableHealthEndpoint bool

	// HealthPath is the path for the health check endpoint.
	// Only used when EnableHealthEndpoint is true. Set via WithHealthPath().
	//
	// Common health check paths:
	//   - "/health" (default)
	//   - "/ping"
	//   - "/status"
	//   - "/healthz" (Kubernetes style)
	//
	// Default: "/health" if EnableHealthEndpoint is true and this is empty.
	HealthPath string

	// Metrics is a custom metrics collector that will be called on each HTTP request.
	// The metrics handler receives the http.Request for each incoming request.
	// Set via WithMetrics().
	//
	// Use for:
	//   - Prometheus metrics collection
	//   - Custom analytics
	//   - Request counting and monitoring
	//   - Performance tracking
	Metrics Metrics

	// MetricsPath is the path for the default metrics endpoint.
	// Only used when EnableDefaultMetrics is true. Set via WithDefaultMetrics().
	//
	// Common metrics paths:
	//   - "/metrics" (default, Prometheus style)
	//   - "/stats"
	//   - "/status/metrics"
	//   - "/monitoring/metrics"
	//
	// Default: "/metrics" if EnableDefaultMetrics is true and this is empty.
	MetricsPath string

	// EnableDefaultMetrics enables the default metrics endpoint.
	EnableDefaultMetrics bool

	// HTTPSRedirect is the HTTPS redirection configuration.
	// When true, all HTTP requests will be automatically redirected to HTTPS.
	HTTPSRedirect HTTPSRedirectConfig

	// CORS is the Cross-Origin Resource Sharing configuration for handling cross-origin requests.
	// Set via WithCORS(), WithCORSAllowOrigins(), or WithCORSConfig().
	//
	// CORS enables web applications running at one domain to access resources from another domain.
	// This is essential for modern web applications, SPAs, and APIs that serve different frontends.
	//
	// Use for:
	//   - API servers serving web applications
	//   - Microservices accessed by different frontends
	//   - Public APIs accessed by third-party applications
	//   - Development servers with frontend/backend separation
	CORS CORSConfig

	// Proxy is the reverse proxy configuration
	Proxy ProxyConfiguration

	// Compression is the HTTP response compression configuration.
	// Set via WithCompression(), WithCompressionConfig(), or other compression options.
	//
	// Controls automatic compression of HTTP response bodies using gzip or deflate encoding.
	// Compression reduces bandwidth usage and improves performance for text-based content.
	//
	// Use for:
	//   - API responses (JSON, XML)
	//   - Static assets (CSS, JS, HTML)
	//   - Large text responses
	//   - Bandwidth optimization
	Compression CompressionConfig
}

Options represents the configuration for a server.

func (*Options) Validate

func (opts *Options) Validate() error

Validate checks the options for consistency and common configuration errors. It returns an error if any configuration is invalid or potentially problematic.

type ProxyConfiguration

type ProxyConfiguration struct {
	// Enabled indicates if the proxy is enabled
	Enabled bool `yaml:"enabled" json:"enabled"`
	// Rules defines the routing rules
	Rules []ProxyRule `yaml:"rules" json:"rules"`
	// GlobalTimeout for all proxy requests
	GlobalTimeout time.Duration `yaml:"global_timeout" json:"global_timeout"`
	// MaxIdleConns for connection pooling
	MaxIdleConns int `yaml:"max_idle_conns" json:"max_idle_conns"`
	// MaxIdleConnsPerHost for connection pooling
	MaxIdleConnsPerHost int `yaml:"max_idle_conns_per_host" json:"max_idle_conns_per_host"`
	// IdleConnTimeout for connection pooling
	IdleConnTimeout time.Duration `yaml:"idle_conn_timeout" json:"idle_conn_timeout"`
	// TrafficDump configuration
	TrafficDump TrafficDumpConfig `yaml:"traffic_dump" json:"traffic_dump"`
	// HealthCheck configuration
	HealthCheck HealthCheckConfig `yaml:"health_check" json:"health_check"`
	// InsecureSkipVerify skips certificate verification
	InsecureSkipVerify bool `yaml:"insecure_skip_verify" json:"insecure_skip_verify"`
}

ProxyConfiguration represents the complete proxy configuration

type ProxyRule

type ProxyRule struct {
	// Name is a unique identifier for the rule
	Name string `yaml:"name" json:"name"`
	// PathPrefix matches request paths starting with this prefix
	PathPrefix string `yaml:"path_prefix" json:"path_prefix"`
	// PathRegex matches request paths using regex (alternative to PathPrefix)
	PathRegex string `yaml:"path_regex" json:"path_regex"`
	// Host matches request Host header
	Host string `yaml:"host" json:"host"`
	// Headers matches specific request headers
	Headers map[string]string `yaml:"headers" json:"headers"`
	// Methods restricts rule to specific HTTP methods
	Methods []string `yaml:"methods" json:"methods"`
	// Backends defines the backend servers for this rule
	Backends []Backend `yaml:"backends" json:"backends"`
	// LoadBalancing strategy for this rule
	LoadBalancing LoadBalancingStrategy `yaml:"load_balancing" json:"load_balancing"`
	// StripPrefix removes prefix from path before forwarding
	StripPrefix string `yaml:"strip_prefix" json:"strip_prefix"`
	// AddPrefix adds prefix to path before forwarding
	AddPrefix string `yaml:"add_prefix" json:"add_prefix"`
	// Timeout for backend requests
	Timeout time.Duration `yaml:"timeout" json:"timeout"`
	// EnableTrafficDump enables traffic dumping for this rule
	EnableTrafficDump bool `yaml:"enable_traffic_dump" json:"enable_traffic_dump"`
	// DumpDirectory specifies where to dump traffic (uses global if empty)
	DumpDirectory string `yaml:"dump_directory" json:"dump_directory"`
	// contains filtered or unexported fields
}

ProxyRule represents a routing rule for the proxy

type RateLimitConfig

type RateLimitConfig struct {
	// Enabled indicates whether rate limiting is enabled.
	Enabled bool

	// RequestsPerInterval is the number of requests allowed per time interval.
	// Set via WithRPM(), WithRPS(), or WithRequestsPerInterval().
	// If not set or zero, rate limiting will be disabled.
	//
	// Common values:
	//   - Public APIs: 60-1000 per minute
	//   - Internal APIs: 1000-10000 per minute
	//   - File uploads: 10-100 per minute
	//   - Authentication: 10-60 per minute
	//
	// The rate limiter uses a token bucket algorithm, refilling tokens at a constant rate.
	RequestsPerInterval int

	// Interval is the time window for the rate limit.
	// Set via WithRPM() (1 minute), WithRPS() (1 second), or WithRequestsPerInterval().
	// If not set, defaults to 1 minute.
	//
	// Common intervals:
	//   - time.Second: For high-frequency APIs
	//   - time.Minute: Most common, good balance
	//   - time.Hour: For very restrictive limits
	//   - 5*time.Minute: Custom business requirements
	//
	// Shorter intervals provide more responsive limiting but require more memory.
	Interval time.Duration

	// BurstSize is the maximum number of requests that can be made immediately.
	// This allows clients to exceed the normal rate limit temporarily by "bursting".
	// Set via WithBurstSize(). If not set, defaults to RequestsPerInterval.
	//
	// How it works:
	//   - Clients can make up to BurstSize requests immediately
	//   - After bursting, they must wait for tokens to refill
	//   - Tokens refill at the configured rate (RequestsPerInterval/Interval)
	//
	// Use cases:
	//   - Handle traffic spikes gracefully
	//   - Allow batch operations
	//   - Improve user experience for bursty clients
	//   - Balance performance with protection
	BurstSize int

	// StatusCode is the HTTP status code returned when rate limit is exceeded.
	// Set via WithRateLimitStatusCode(). Defaults to 429 (Too Many Requests) if not set.
	//
	// Common status codes:
	//   - 429 Too Many Requests (recommended)
	//   - 503 Service Unavailable
	//   - 502 Bad Gateway (for proxy scenarios)
	//
	// The 429 status code is specifically designed for rate limiting and is
	// understood by most HTTP clients and libraries.
	StatusCode int

	// Message is the response body returned when rate limit is exceeded.
	// Set via WithRateLimitMessage(). Defaults to "rate limit exceeded, try again later." if not set.
	//
	// Best practices:
	//   - Be clear about the limit
	//   - Suggest when to retry
	//   - Keep messages user-friendly
	//   - Include contact information for questions
	//
	// The message is returned as plain text in the response body.
	Message string

	// KeyFunc is a custom function to extract the rate limit key from requests.
	// This determines how clients are identified for rate limiting purposes.
	// Set via WithRateLimitKeyFunc().
	//
	// Default behavior uses client IP address. Custom key functions enable:
	//   - User-based rate limiting (requires authentication)
	//   - API key-based limits
	//   - Different limits for different client types
	//   - Combined identification strategies
	//
	// Example functions:
	//   - IP-based: func(r *http.Request) string { return r.RemoteAddr }
	//   - User-based: func(r *http.Request) string { return getUserID(r) }
	//   - API key-based: func(r *http.Request) string { return r.Header.Get("X-API-Key") }
	KeyFunc func(r *http.Request) string

	// ExcludePaths are paths that should be excluded from rate limiting.
	// Requests to these paths will not be counted against rate limits.
	// Set via WithRateLimitExcludePaths().
	//
	// Common exclusions:
	//   - Health checks: "/health", "/ping"
	//   - Metrics: "/metrics", "/stats"
	//   - Static files: "/static/*", "/assets/*"
	//   - Documentation: "/docs/*", "/swagger/*"
	//   - Infrastructure: "/robots.txt", "/favicon.ico"
	//
	// Path matching supports wildcards (*) for pattern matching.
	ExcludePaths []string

	// IncludePaths are paths that should be included in rate limiting.
	// If set, only requests to these paths will be rate limited. All other paths are excluded.
	// Set via WithRateLimitIncludePaths().
	//
	// If both IncludePaths and ExcludePaths are set:
	//   1. Paths must match IncludePaths to be rate limited
	//   2. Paths in ExcludePaths are then excluded from rate limiting
	//
	// Use cases:
	//   - Protect only sensitive endpoints
	//   - Apply different limits to different API versions
	//   - Rate limit only external-facing endpoints
	//   - Granular control over protection
	IncludePaths []string

	// TrustedProxies is a list of trusted proxy IP addresses or CIDR ranges
	// for accurate client IP detection in rate limiting.
	// Set via WithRateLimitTrustedProxies().
	//
	// How it works:
	//   - Without trusted proxies: Uses r.RemoteAddr (proxy IP)
	//   - With trusted proxies: Uses X-Forwarded-For or X-Real-IP headers
	//
	// Common proxy ranges:
	//   - AWS ALB: Check AWS documentation for current ranges
	//   - Cloudflare: Use Cloudflare's published IP ranges
	//   - Internal load balancers: Your internal network ranges
	//   - Docker networks: 172.16.0.0/12, 10.0.0.0/8
	//
	// Security note: Only list IPs you actually trust. Malicious clients
	// can spoof X-Forwarded-For headers if the proxy IP is trusted.
	TrustedProxies []string
}

RateLimitConfig holds configuration for the rate limiter middleware. This controls request frequency per client using a token bucket algorithm.

Rate limiting helps protect your server from:

  • DDoS attacks
  • Brute force attempts
  • Resource exhaustion
  • Abusive clients

Example configurations:

// API server: 100 requests per minute with burst of 20
rateLimit := RateLimitConfig{
	RequestsPerInterval: 100,
	Interval:           time.Minute,
	BurstSize:          20,
	StatusCode:         429,
	Message:           "Rate limit exceeded. Try again later.",
}

// High-security: 10 requests per second, no burst
rateLimit := RateLimitConfig{
	RequestsPerInterval: 10,
	Interval:           time.Second,
	BurstSize:          1,
}

type RateLimitConfiguration

type RateLimitConfiguration struct {
	Enabled             bool          `yaml:"enabled" json:"enabled" env:"SERVEX_RATE_LIMIT_ENABLED"`
	RequestsPerInterval int           `yaml:"requests_per_interval" json:"requests_per_interval" env:"SERVEX_RATE_LIMIT_REQUESTS_PER_INTERVAL"`
	Interval            time.Duration `yaml:"interval" json:"interval" env:"SERVEX_RATE_LIMIT_INTERVAL"`
	BurstSize           int           `yaml:"burst_size" json:"burst_size" env:"SERVEX_RATE_LIMIT_BURST_SIZE"`
	StatusCode          int           `yaml:"status_code" json:"status_code" env:"SERVEX_RATE_LIMIT_STATUS_CODE"`
	Message             string        `yaml:"message" json:"message" env:"SERVEX_RATE_LIMIT_MESSAGE"`
	ExcludePaths        []string      `yaml:"exclude_paths" json:"exclude_paths" env:"SERVEX_RATE_LIMIT_EXCLUDE_PATHS"`
	IncludePaths        []string      `yaml:"include_paths" json:"include_paths" env:"SERVEX_RATE_LIMIT_INCLUDE_PATHS"`
	TrustedProxies      []string      `yaml:"trusted_proxies" json:"trusted_proxies" env:"SERVEX_RATE_LIMIT_TRUSTED_PROXIES"`
}

RateLimitConfiguration represents rate limiting configuration

type RequestLogBundle

type RequestLogBundle struct {
	Request           *http.Request
	RequestID         string
	Error             error
	ErrorMessage      string
	StatusCode        int
	StartTime         time.Time
	NoLogClientErrors bool
}

RequestLogBundle represents a bundle of information for logging a request.

type RequestLogger

type RequestLogger interface {
	Log(RequestLogBundle)
}

RequestLogger is an interface for logging requests. [RequestLogger.Log] is called at the end of each request after returning from handler.

type RoleContextKey

type RoleContextKey struct{}

type SecurityConfig

type SecurityConfig struct {
	// Enabled determines whether security headers middleware is active.
	// Must be set to true for any security headers to be applied.
	// Set via WithSecurityHeaders(), WithStrictSecurityHeaders(), or WithSecurityConfig().
	//
	// When disabled, no security headers will be added to responses,
	// even if individual header values are configured.
	Enabled bool

	// CSRFEnabled determines whether CSRF protection is active.
	// When enabled, CSRF tokens are required for POST, PUT, PATCH, DELETE requests.
	// Set via WithCSRFProtection(), WithStrictSecurityHeaders(), or WithSecurityConfig().
	//
	// CSRF protection works by:
	//   1. Generating a secure random token for each session
	//   2. Setting the token in a cookie and/or providing it via an endpoint
	//   3. Requiring the token in a header or form field for state-changing requests
	//   4. Validating that the token matches the expected value
	//
	// Use cases:
	//   - Web applications with forms and AJAX requests
	//   - APIs that accept requests from browsers
	//   - Any application vulnerable to CSRF attacks
	CSRFEnabled bool

	// CSRFTokenName is the name of the CSRF token in headers and form fields.
	// Set via WithCSRFTokenName() or WithSecurityConfig().
	//
	// Common names:
	//   - "X-CSRF-Token" (default, Rails/Django style)
	//   - "X-XSRF-TOKEN" (Angular style)
	//   - "csrf_token" (for form fields)
	//   - "_token" (Laravel style)
	//
	// The middleware will look for the token in:
	//   1. Request header with this name
	//   2. Form field with this name (for multipart/form-data and application/x-www-form-urlencoded)
	//   3. URL query parameter with this name (as fallback)
	CSRFTokenName string

	// CSRFCookieName is the name of the cookie that stores the CSRF token.
	// Set via WithCSRFCookieName() or WithSecurityConfig().
	//
	// Common names:
	//   - "csrf_token" (default)
	//   - "XSRF-TOKEN" (Angular style, readable by JavaScript)
	//   - "_csrf" (Express.js style)
	//
	// The cookie is used to store the expected CSRF token value.
	// For maximum security, set CSRFCookieHttpOnly to true.
	CSRFCookieName string

	// CSRFCookieHttpOnly determines if the CSRF cookie is HTTP-only.
	// Set via WithCSRFCookieHttpOnly() or WithSecurityConfig().
	//
	// Security trade-offs:
	//   - true (recommended): More secure, prevents XSS token theft, but requires server-side token injection
	//   - false: Allows JavaScript access, enables SPA token retrieval, but vulnerable to XSS
	//
	// When true:
	//   - Use CSRFTokenEndpoint to provide tokens to JavaScript
	//   - Inject tokens into HTML templates server-side
	//
	// When false:
	//   - JavaScript can read document.cookie to get the token
	//   - Useful for SPAs and AJAX-heavy applications
	CSRFCookieHttpOnly bool

	// CSRFCookieSameSite sets the SameSite attribute for the CSRF cookie.
	// Set via WithCSRFCookieSameSite() or WithSecurityConfig().
	//
	// Options:
	//   - "Strict": Maximum protection, may break some legitimate cross-site usage
	//   - "Lax" (recommended): Good protection with better usability
	//   - "None": Least protection, requires Secure=true, allows cross-site requests
	//
	// "Lax" provides good CSRF protection while maintaining usability.
	CSRFCookieSameSite string

	// CSRFCookieSecure determines if the CSRF cookie requires HTTPS.
	// Set via WithCSRFCookieSecure() or WithSecurityConfig().
	//
	// Recommendations:
	//   - true: Required for production HTTPS sites
	//   - false: Only for development with HTTP
	//
	// Automatically set to true when SameSite=None.
	CSRFCookieSecure bool

	// CSRFCookiePath sets the path attribute for the CSRF cookie.
	// Set via WithCSRFCookiePath() or WithSecurityConfig().
	//
	// Common values:
	//   - "/" (default): Cookie available for entire site
	//   - "/app": Cookie only for application section
	//   - "/api": Cookie only for API endpoints
	//
	// Use specific paths to limit cookie scope and improve security.
	CSRFCookiePath string

	// CSRFCookieMaxAge sets the maximum age for the CSRF cookie in seconds.
	// Set via WithCSRFCookieMaxAge() or WithSecurityConfig().
	//
	// Common values:
	//   - 3600: 1 hour (short-lived, more secure)
	//   - 86400: 1 day (balance of security and usability)
	//   - 604800: 1 week (longer sessions)
	//   - 0: Session cookie (expires when browser closes)
	//
	// Shorter durations improve security but may affect user experience.
	CSRFCookieMaxAge int

	// CSRFTokenEndpoint enables an endpoint to retrieve CSRF tokens via AJAX.
	// Set via WithCSRFTokenEndpoint() or WithSecurityConfig().
	//
	// When set, creates an endpoint (e.g., "/csrf-token") that returns:
	//   {"csrf_token": "abc123..."}
	//
	// Use cases:
	//   - SPAs that need to fetch tokens dynamically
	//   - AJAX applications with HttpOnly cookies
	//   - Mobile apps that need CSRF tokens
	//
	// The endpoint:
	//   - Uses GET method
	//   - Returns JSON with the token
	//   - Sets the CSRF cookie
	//   - Bypasses CSRF validation (safe since it's read-only)
	CSRFTokenEndpoint string

	// CSRFErrorMessage is the message returned when CSRF validation fails.
	// Set via WithCSRFErrorMessage() or WithSecurityConfig().
	//
	// Default: "CSRF token validation failed"
	//
	// Best practices:
	//   - Keep messages generic to avoid information disclosure
	//   - Include guidance for legitimate users
	//   - Consider localization for international applications
	CSRFErrorMessage string

	// CSRFSafeMethods lists HTTP methods that bypass CSRF validation.
	// Set via WithCSRFSafeMethods() or WithSecurityConfig().
	//
	// Default: [GET, "HEAD", OPTIONS, "TRACE"]
	//
	// These methods are considered safe because they shouldn't have side effects.
	// POST, PUT, PATCH, DELETE require CSRF tokens by default.
	//
	// Only modify this if you have specific requirements.
	CSRFSafeMethods []string

	// ContentSecurityPolicy sets the Content-Security-Policy header for XSS protection.
	// This header controls which resources the browser is allowed to load.
	// Set via WithContentSecurityPolicy() or WithStrictSecurityHeaders().
	//
	// Common policies:
	//   - Basic: "default-src 'self'"
	//   - With inline scripts: "default-src 'self'; script-src 'self' 'unsafe-inline'"
	//   - Strict: "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'"
	//   - API-only: "default-src 'none'; frame-ancestors 'none'"
	//
	// Use CSP generators or testing tools to create appropriate policies.
	// Start with a restrictive policy and gradually add exceptions as needed.
	ContentSecurityPolicy string

	// XContentTypeOptions sets the X-Content-Type-Options header to prevent MIME sniffing.
	// This prevents browsers from interpreting files differently than declared by Content-Type.
	// Set via WithSecurityHeaders(), WithStrictSecurityHeaders(), or WithSecurityConfig().
	//
	// Standard value: "nosniff"
	//
	// Benefits:
	//   - Prevents MIME confusion attacks
	//   - Ensures Content-Type headers are respected
	//   - Reduces risk of drive-by downloads
	//   - Essential for file upload applications
	XContentTypeOptions string

	// XFrameOptions sets the X-Frame-Options header to prevent clickjacking attacks.
	// This controls whether the page can be displayed in frames/iframes.
	// Set via WithSecurityHeaders(), WithStrictSecurityHeaders(), or WithSecurityConfig().
	//
	// Options:
	//   - "DENY": Page cannot be framed at all
	//   - "SAMEORIGIN": Page can only be framed by same origin
	//   - "ALLOW-FROM uri": Page can only be framed by specified URI
	//
	// Use "DENY" for maximum security unless you specifically need framing.
	XFrameOptions string

	// XXSSProtection sets the X-XSS-Protection header for legacy XSS protection.
	// Modern browsers rely more on CSP, but this provides additional protection.
	// Set via WithSecurityHeaders(), WithStrictSecurityHeaders(), or WithSecurityConfig().
	//
	// Common values:
	//   - "1": Enable XSS filtering (basic)
	//   - "1; mode=block": Enable XSS filtering and block rather than sanitize
	//   - "0": Disable XSS filtering (not recommended)
	//
	// Note: This header is deprecated in favor of CSP but still useful for older browsers.
	XXSSProtection string

	// StrictTransportSecurity sets the HSTS header to enforce HTTPS usage.
	// This tells browsers to only access the site over HTTPS for a specified time.
	// Set via WithHSTSHeader(), WithStrictSecurityHeaders(), or WithSecurityConfig().
	//
	// Format: "max-age=<seconds>; includeSubDomains; preload"
	//
	// Common configurations:
	//   - Basic: "max-age=31536000" (1 year)
	//   - With subdomains: "max-age=31536000; includeSubDomains"
	//   - Maximum security: "max-age=63072000; includeSubDomains; preload" (2 years)
	//
	// Only set this if your site fully supports HTTPS and you're ready to commit to it.
	StrictTransportSecurity string

	// ReferrerPolicy sets the Referrer-Policy header to control referrer information.
	// This controls how much referrer information is sent with requests.
	// Set via WithStrictSecurityHeaders() or WithSecurityConfig().
	//
	// Options:
	//   - "no-referrer": Never send referrer information
	//   - "same-origin": Send referrer only for same-origin requests
	//   - "strict-origin": Send origin only, and only for HTTPS-to-HTTPS
	//   - "strict-origin-when-cross-origin": Full URL for same-origin, origin only for cross-origin
	//   - "unsafe-url": Always send full URL (not recommended)
	//
	// Balance privacy protection with functionality needs.
	ReferrerPolicy string

	// PermissionsPolicy sets the Permissions-Policy header to control browser features.
	// This restricts access to browser APIs and features for enhanced privacy/security.
	// Set via WithStrictSecurityHeaders() or WithSecurityConfig().
	//
	// Format: "feature=(allowlist)"
	//
	// Examples:
	//   - Block all: "geolocation=(), microphone=(), camera=(), payment=(), usb=()"
	//   - Self only: "geolocation=(self), microphone=(self), camera=(self)"
	//   - Specific origins: "geolocation=(\"https://maps.example.com\")"
	//
	// Common features: geolocation, microphone, camera, payment, usb, magnetometer, gyroscope
	PermissionsPolicy string

	// XPermittedCrossDomainPolicies sets the X-Permitted-Cross-Domain-Policies header.
	// This controls cross-domain access for Flash and PDF files.
	// Set via WithStrictSecurityHeaders() or WithSecurityConfig().
	//
	// Options:
	//   - "none": No cross-domain access allowed (recommended)
	//   - "master-only": Only master policy file is allowed
	//   - "by-content-type": Policy files served with appropriate content type
	//   - "all": All policy files allowed (not recommended)
	//
	// Use "none" unless you specifically need cross-domain Flash/PDF functionality.
	XPermittedCrossDomainPolicies string

	// CrossOriginEmbedderPolicy sets the Cross-Origin-Embedder-Policy header.
	// This header allows a document to control which cross-origin resources can be embedded.
	// Set via WithStrictSecurityHeaders() or WithSecurityConfig().
	//
	// Options:
	//   - "require-corp": Embedded resources must explicitly opt-in to being embedded
	//   - "unsafe-none": No restrictions (default behavior)
	//
	// Use "require-corp" for applications that need to isolate their context
	// from potentially malicious cross-origin resources.
	CrossOriginEmbedderPolicy string

	// CrossOriginOpenerPolicy sets the Cross-Origin-Opener-Policy header.
	// This header controls the opener relationship for windows opened via links.
	// Set via WithStrictSecurityHeaders() or WithSecurityConfig().
	//
	// Options:
	//   - "same-origin": Retain opener only for same-origin navigation
	//   - "same-origin-allow-popups": Like same-origin but allows popups
	//   - "unsafe-none": No restrictions (default)
	//
	// Use "same-origin" to prevent cross-origin pages from accessing your window object.
	CrossOriginOpenerPolicy string

	// CrossOriginResourcePolicy sets the Cross-Origin-Resource-Policy header.
	// This header controls which cross-origin requests can include this resource.
	// Set via WithStrictSecurityHeaders() or WithSecurityConfig().
	//
	// Options:
	//   - "same-site": Resource can be loaded by same-site requests only
	//   - "same-origin": Resource can be loaded by same-origin requests only
	//   - "cross-origin": Resource can be loaded by any origin
	//
	// Use "same-site" or "same-origin" for sensitive resources that shouldn't
	// be embeddable by other origins.
	CrossOriginResourcePolicy string

	// ExcludePaths are paths that should be excluded from security headers.
	// Requests to these paths will not have security headers applied.
	// Set via WithSecurityExcludePaths().
	//
	// Common exclusions:
	//   - API endpoints that need different policies: "/api/*"
	//   - Legacy applications: "/legacy/*"
	//   - Third-party integrations: "/webhooks/*"
	//   - Public assets that need embedding: "/public/*"
	//   - Development tools: "/debug/*"
	//
	// Path matching supports wildcards (*) for pattern matching.
	ExcludePaths []string

	// IncludePaths are paths that should have security headers applied.
	// If set, only requests to these paths will receive security headers.
	// Set via WithSecurityIncludePaths().
	//
	// If both IncludePaths and ExcludePaths are set:
	//   1. Paths must match IncludePaths to receive security headers
	//   2. Paths in ExcludePaths are then excluded from security headers
	//
	// Use cases:
	//   - Apply security headers only to web UI: "/app/*", "/dashboard/*"
	//   - Secure only public-facing endpoints: "/public/*"
	//   - Protect specific application sections: "/admin/*", "/user/*"
	//
	// Path matching supports wildcards (*) for pattern matching.
	IncludePaths []string
}

SecurityConfig holds configuration for security headers middleware. SecurityConfig holds configuration for security headers middleware. These headers protect web applications from common security vulnerabilities.

Security headers help prevent:

  • Cross-site scripting (XSS) attacks
  • Clickjacking attacks
  • MIME type sniffing vulnerabilities
  • Cross-origin policy violations
  • Content injection attacks

Example configuration:

security := SecurityConfig{
	Enabled: true,
	ContentSecurityPolicy: "default-src 'self'; script-src 'self' 'unsafe-inline'",
	XFrameOptions: "DENY",
	XContentTypeOptions: "nosniff",
	StrictTransportSecurity: "max-age=31536000; includeSubDomains",
}

Use WithStrictSecurityHeaders() for a preset of maximum security headers.

type SecurityConfiguration

type SecurityConfiguration struct {
	Enabled bool `yaml:"enabled" json:"enabled" env:"SERVEX_SECURITY_ENABLED"`

	// CSRF Protection Configuration
	CSRFEnabled        bool     `yaml:"csrf_enabled" json:"csrf_enabled" env:"SERVEX_SECURITY_CSRF_ENABLED"`
	CSRFTokenName      string   `yaml:"csrf_token_name" json:"csrf_token_name" env:"SERVEX_SECURITY_CSRF_TOKEN_NAME"`
	CSRFCookieName     string   `yaml:"csrf_cookie_name" json:"csrf_cookie_name" env:"SERVEX_SECURITY_CSRF_COOKIE_NAME"`
	CSRFCookieHttpOnly bool     `yaml:"csrf_cookie_http_only" json:"csrf_cookie_http_only" env:"SERVEX_SECURITY_CSRF_COOKIE_HTTP_ONLY"`
	CSRFCookieSameSite string   `yaml:"csrf_cookie_same_site" json:"csrf_cookie_same_site" env:"SERVEX_SECURITY_CSRF_COOKIE_SAME_SITE"`
	CSRFCookieSecure   bool     `yaml:"csrf_cookie_secure" json:"csrf_cookie_secure" env:"SERVEX_SECURITY_CSRF_COOKIE_SECURE"`
	CSRFCookiePath     string   `yaml:"csrf_cookie_path" json:"csrf_cookie_path" env:"SERVEX_SECURITY_CSRF_COOKIE_PATH"`
	CSRFCookieMaxAge   int      `yaml:"csrf_cookie_max_age" json:"csrf_cookie_max_age" env:"SERVEX_SECURITY_CSRF_COOKIE_MAX_AGE"`
	CSRFTokenEndpoint  string   `yaml:"csrf_token_endpoint" json:"csrf_token_endpoint" env:"SERVEX_SECURITY_CSRF_TOKEN_ENDPOINT"`
	CSRFErrorMessage   string   `yaml:"csrf_error_message" json:"csrf_error_message" env:"SERVEX_SECURITY_CSRF_ERROR_MESSAGE"`
	CSRFSafeMethods    []string `yaml:"csrf_safe_methods" json:"csrf_safe_methods" env:"SERVEX_SECURITY_CSRF_SAFE_METHODS"`

	// Security Headers Configuration
	ContentSecurityPolicy         string   `yaml:"content_security_policy" json:"content_security_policy" env:"SERVEX_SECURITY_CONTENT_SECURITY_POLICY"`
	XContentTypeOptions           string   `yaml:"x_content_type_options" json:"x_content_type_options" env:"SERVEX_SECURITY_X_CONTENT_TYPE_OPTIONS"`
	XFrameOptions                 string   `yaml:"x_frame_options" json:"x_frame_options" env:"SERVEX_SECURITY_X_FRAME_OPTIONS"`
	XXSSProtection                string   `yaml:"x_xss_protection" json:"x_xss_protection" env:"SERVEX_SECURITY_X_XSS_PROTECTION"`
	StrictTransportSecurity       string   `yaml:"strict_transport_security" json:"strict_transport_security" env:"SERVEX_SECURITY_STRICT_TRANSPORT_SECURITY"`
	ReferrerPolicy                string   `yaml:"referrer_policy" json:"referrer_policy" env:"SERVEX_SECURITY_REFERRER_POLICY"`
	PermissionsPolicy             string   `yaml:"permissions_policy" json:"permissions_policy" env:"SERVEX_SECURITY_PERMISSIONS_POLICY"`
	XPermittedCrossDomainPolicies string   `` /* 137-byte string literal not displayed */
	CrossOriginEmbedderPolicy     string   `yaml:"cross_origin_embedder_policy" json:"cross_origin_embedder_policy" env:"SERVEX_SECURITY_CROSS_ORIGIN_EMBEDDER_POLICY"`
	CrossOriginOpenerPolicy       string   `yaml:"cross_origin_opener_policy" json:"cross_origin_opener_policy" env:"SERVEX_SECURITY_CROSS_ORIGIN_OPENER_POLICY"`
	CrossOriginResourcePolicy     string   `yaml:"cross_origin_resource_policy" json:"cross_origin_resource_policy" env:"SERVEX_SECURITY_CROSS_ORIGIN_RESOURCE_POLICY"`
	ExcludePaths                  []string `yaml:"exclude_paths" json:"exclude_paths" env:"SERVEX_SECURITY_EXCLUDE_PATHS"`
	IncludePaths                  []string `yaml:"include_paths" json:"include_paths" env:"SERVEX_SECURITY_INCLUDE_PATHS"`
}

SecurityConfiguration represents security headers configuration

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server represents a high-performance HTTP/HTTPS server with built-in middleware support.

The Server provides comprehensive features including:

  • Authentication and authorization with JWT tokens
  • Rate limiting with multiple strategies
  • Request filtering (IP, User-Agent, headers, query params)
  • Security headers and CSRF protection
  • Static file serving with SPA support
  • Proxy/gateway functionality with load balancing
  • Request logging and metrics collection
  • Cache control headers
  • Graceful shutdown support

Server instances are created using New() or NewWithOptions() and configured through the Options pattern using With* functions.

Example usage:

server, err := servex.New(
	servex.WithAuthToken("secret-token"),
	servex.WithRateLimitRPM(60),
	servex.WithSecurityHeaders(),
)
if err != nil {
	log.Fatal(err)
}

server.HandleFunc("/api/hello", func(w http.ResponseWriter, r *http.Request) {
	servex.C(w, r).Response(200, map[string]string{"message": "Hello, World!"})
})

log.Fatal(server.Start(":8080", ""))

func NewServer

func NewServer(ops ...Option) (*Server, error)

NewServer creates a new Server instance with the specified options.

The server is configured using the Options pattern with With* functions. A server without a TLS certificate can only serve plain HTTP traffic.

Available configuration options include:

  • Authentication: WithAuth(), WithAuthToken(), WithAuthMemoryDatabase()
  • Rate limiting: WithRPM(), WithRPS(), WithRateLimitConfig()
  • Security: WithSecurityHeaders(), WithCSRFProtection(), WithStrictSecurityHeaders()
  • Filtering: WithAllowedIPs(), WithBlockedUserAgents(), WithFilterConfig()
  • Static files: WithStaticFiles(), WithSPAMode()
  • Logging: WithLogger(), WithRequestLogger(), WithAuditLogger()
  • TLS: WithCertificate(), WithCertificateFromFile()
  • And many more...

Example:

server, err := servex.NewServer(
	servex.WithAuthToken("my-secret-token"),
	servex.WithRateLimitRPM(100),
	servex.WithSecurityHeaders(),
	servex.WithStaticFiles("./public"),
)
if err != nil {
	return fmt.Errorf("create server: %w", err)
}

The server must have routes registered using the router methods (HandleFunc, GET, POST, etc.) before starting.

func NewServerFromConfig

func NewServerFromConfig(config *Config) (*Server, error)

NewServerFromConfig creates a new Server instance from a Config struct

func NewServerWithOptions

func NewServerWithOptions(opts Options) (*Server, error)

NewServerWithOptions creates a new Server instance with the provided Options struct.

This function is useful when you have already constructed an Options struct, either programmatically or from configuration files. For most use cases, New() with With* functions is more convenient.

The function validates the configuration and returns an error if any invalid settings are detected.

Example:

opts := servex.Options{
	AuthToken: "my-secret-token",
	RateLimit: servex.RateLimitConfig{
		Enabled: true,
		RequestsPerInterval: 100,
		Interval: time.Minute,
	},
	Security: servex.SecurityConfig{
		Enabled: true,
		XContentTypeOptions: "nosniff",
	},
}

server, err := servex.NewServerWithOptions(opts)
if err != nil {
	return fmt.Errorf("create server: %w", err)
}

func (*Server) AddMiddlewares

func (s *Server) AddMiddlewares(middleware ...func(http.Handler) http.Handler)

AddMiddlewares adds one or more mux.MiddlewareFunc to the router.

func (*Server) AddStaticFileRoutes

func (s *Server) AddStaticFileRoutes(cfg StaticFileConfig) error

AddStaticFileRoutes is a convenience method to add static file serving to a router. This can be used as an alternative to middleware if you prefer explicit routing.

func (*Server) AuthManager

func (s *Server) AuthManager() *AuthManager

AuthManager returns the server's authentication manager for manual auth operations.

This provides access to the underlying AuthManager for advanced authentication use cases such as:

  • Creating users programmatically
  • Validating tokens manually
  • Custom authentication flows
  • User management operations

Returns nil if authentication is not enabled (no database configured).

Example:

if authMgr := server.AuthManager(); authMgr != nil {
	ctx := context.Background()
	err := authMgr.CreateUser(ctx, "admin", "secure-password", "admin", "user")
	if err != nil {
		log.Printf("Failed to create user: %v", err)
	}
}

func (*Server) C

C returns a new context for the provided request. It is a shortcut for C with server options.

func (*Server) CONNECT

func (s *Server) CONNECT(path string, h http.HandlerFunc) *mux.Route

CONNECT is an alias for Server.Connect.

func (*Server) Connect

func (s *Server) Connect(path string, h http.HandlerFunc) *mux.Route

Connect registers a new CONNECT route with the provided path and http.HandlerFunc. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) ConnectWithAuth

func (s *Server) ConnectWithAuth(path string, h http.HandlerFunc, roles ...UserRole) *mux.Route

ConnectWithAuth registers a new CONNECT route with the provided path and http.HandlerFunc. It adds auth middleware to the route with the provided roles. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) DELETE

func (s *Server) DELETE(path string, h http.HandlerFunc) *mux.Route

DELETE is an alias for Server.Delete.

func (*Server) Delete

func (s *Server) Delete(path string, h http.HandlerFunc) *mux.Route

Delete registers a new DELETE route with the provided path and http.HandlerFunc. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) DeleteWithAuth

func (s *Server) DeleteWithAuth(path string, h http.HandlerFunc, roles ...UserRole) *mux.Route

DeleteWithAuth registers a new DELETE route with the provided path and http.HandlerFunc. It adds auth middleware to the route with the provided roles. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) Filter

func (s *Server) Filter() DynamicFilterMethods

Filter returns the active filter instance for dynamic modification. Returns nil if no filter is configured or enabled.

Example:

// Block an IP that accessed a honeypot
if filter := server.Filter(); filter != nil {
    err := filter.AddBlockedIP("192.168.1.100")
    if err != nil {
        log.Printf("Failed to block IP: %v", err)
    }
}

// Check if a User-Agent is blocked
if filter := server.Filter(); filter != nil {
    if filter.IsUserAgentBlocked("BadBot/1.0") {
        log.Println("User-Agent is blocked")
    }
}

func (*Server) GET

func (s *Server) GET(path string, h http.HandlerFunc) *mux.Route

GET is an alias for Server.Get.

func (*Server) Get

func (s *Server) Get(path string, h http.HandlerFunc) *mux.Route

Get registers a new GET route with the provided path and http.HandlerFunc. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) GetWithAuth

func (s *Server) GetWithAuth(path string, h http.HandlerFunc, roles ...UserRole) *mux.Route

GetWithAuth registers a new GET route with the provided path and http.HandlerFunc. It adds auth middleware to the route with the provided roles. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) H

func (s *Server) H(path string, h http.Handler, methods ...string) *mux.Route

H is a shortcut for Server.Handle.

Parameters:

  • path: The path to register the route for
  • h: The handler to register the route for
  • methods: The methods to register the route for

Returns:

  • *mux.Route: The created route to set additional settings to the route

func (*Server) HA

func (s *Server) HA(path string, f http.HandlerFunc, roles ...UserRole) *mux.Route

HA is a shortcut for Server.HandleWithAuth.

func (*Server) HEAD

func (s *Server) HEAD(path string, h http.HandlerFunc) *mux.Route

HEAD is an alias for Server.Head.

func (*Server) HF

func (s *Server) HF(path string, f http.HandlerFunc, methods ...string) *mux.Route

HF is a shortcut for Server.HandleFunc.

Parameters:

  • path: The path to register the route for
  • f: The handler to register the route for
  • methods: The methods to register the route for

Example:

server.HF("/users", func(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello, World!"))
}, GET, POST)

Returns:

  • *mux.Route: The created route to set additional settings to the route

func (*Server) HFA

func (s *Server) HFA(path string, f http.HandlerFunc, roles ...UserRole) *mux.Route

HFA is a shortcut for Server.HandleFuncWithAuth.

func (*Server) HTTPAddress

func (s *Server) HTTPAddress() string

HTTPAddress returns the address the HTTP server is listening on. Returns an empty string if the HTTP server is not running or not configured.

func (*Server) HTTPSAddress

func (s *Server) HTTPSAddress() string

HTTPSAddress returns the address the HTTPS server is listening on. Returns an empty string if the HTTPS server is not running or not configured.

func (*Server) Handle

func (s *Server) Handle(path string, h http.Handler, methods ...string) *mux.Route

Handle registers a new route with the provided path, http.Handler and methods. It returns a pointer to the created mux.Route to set additional settings to the route.

Parameters:

  • path: The path to register the route for
  • h: The handler to register the route for
  • methods: The methods to register the route for

Returns:

  • *mux.Route: The created route to set additional settings to the route

func (*Server) HandleFunc

func (s *Server) HandleFunc(path string, f http.HandlerFunc, methods ...string) *mux.Route

HandleFunc registers a new route with the provided path, http.HandlerFunc and methods. It returns a pointer to the created mux.Route to set additional settings to the route.

Parameters:

  • path: The path to register the route for
  • f: The handler to register the route for
  • methods: The methods to register the route for

Example:

server.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello, World!"))
}, GET, POST)

Returns:

  • *mux.Route: The created route to set additional settings to the route

func (*Server) HandleFuncWithAuth

func (s *Server) HandleFuncWithAuth(path string, f http.HandlerFunc, roles ...UserRole) *mux.Route

HandleFuncWithAuth registers a new route with the provided path, http.HandlerFunc and methods. It adds auth middleware to the route with the provided roles. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) HandleWithAuth

func (s *Server) HandleWithAuth(path string, h http.Handler, roles ...UserRole) *mux.Route

HandleWithAuth registers a new route with the provided path, http.Handler and methods. It adds auth middleware to the route with the provided roles. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) Head

func (s *Server) Head(path string, h http.HandlerFunc) *mux.Route

Head registers a new HEAD route with the provided path and http.HandlerFunc. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) HeadWithAuth

func (s *Server) HeadWithAuth(path string, h http.HandlerFunc, roles ...UserRole) *mux.Route

HeadWithAuth registers a new HEAD route with the provided path and http.HandlerFunc. It adds auth middleware to the route with the provided roles. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) IsAuthEnabled

func (s *Server) IsAuthEnabled() bool

IsAuthEnabled returns true if authentication is enabled on this server.

Authentication is enabled when a database is configured through WithAuth() or WithAuthMemoryDatabase() options.

func (*Server) IsHTTP

func (s *Server) IsHTTP() bool

IsHTTP returns true if the HTTP server is running.

This indicates that the HTTP server has been started successfully and is accepting connections.

func (*Server) IsTLS

func (s *Server) IsTLS() bool

IsTLS returns true if the HTTPS server is running.

This indicates that the server has a TLS certificate configured and the HTTPS server has been started successfully.

func (*Server) NewContext

func (s *Server) NewContext(w http.ResponseWriter, r *http.Request) *Context

NewContext returns a new context for the provided request. It is a shortcut for NewContext with server options.

func (*Server) OPTIONS

func (s *Server) OPTIONS(path string, h http.HandlerFunc) *mux.Route

OPTIONS is an alias for Server.Options.

func (*Server) Options

func (s *Server) Options(path string, h http.HandlerFunc) *mux.Route

Options registers a new OPTIONS route with the provided path and http.HandlerFunc. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) OptionsWithAuth

func (s *Server) OptionsWithAuth(path string, h http.HandlerFunc, roles ...UserRole) *mux.Route

OptionsWithAuth registers a new OPTIONS route with the provided path and http.HandlerFunc. It adds auth middleware to the route with the provided roles. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) PATCH

func (s *Server) PATCH(path string, h http.HandlerFunc) *mux.Route

PATCH is an alias for Server.Patch.

func (*Server) POST

func (s *Server) POST(path string, h http.HandlerFunc) *mux.Route

POST is an alias for Server.Post.

func (*Server) PUT

func (s *Server) PUT(path string, h http.HandlerFunc) *mux.Route

PUT is an alias for Server.Put.

func (*Server) Patch

func (s *Server) Patch(path string, h http.HandlerFunc) *mux.Route

Patch registers a new PATCH route with the provided path and http.HandlerFunc. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) PatchWithAuth

func (s *Server) PatchWithAuth(path string, h http.HandlerFunc, roles ...UserRole) *mux.Route

PatchWithAuth registers a new PATCH route with the provided path and http.HandlerFunc. It adds auth middleware to the route with the provided roles. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) Post

func (s *Server) Post(path string, h http.HandlerFunc) *mux.Route

Post registers a new POST route with the provided path and http.HandlerFunc. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) PostWithAuth

func (s *Server) PostWithAuth(path string, h http.HandlerFunc, roles ...UserRole) *mux.Route

PostWithAuth registers a new POST route with the provided path and http.HandlerFunc. It adds auth middleware to the route with the provided roles. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) Put

func (s *Server) Put(path string, h http.HandlerFunc) *mux.Route

Put registers a new PUT route with the provided path and http.HandlerFunc. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) PutWithAuth

func (s *Server) PutWithAuth(path string, h http.HandlerFunc, roles ...UserRole) *mux.Route

PutWithAuth registers a new PUT route with the provided path and http.HandlerFunc. It adds auth middleware to the route with the provided roles. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) R

func (s *Server) R(path ...string) *mux.Router

R is a shortcut for Server.Router.

func (*Server) RemoveBasePath added in v2.3.0

func (s *Server) RemoveBasePath() *Server

RemoveBasePath clears the base path for the server's router. It returns the server itself to allow method chaining.

Example:

server.WithBasePath("/api")
server.Get("/users", ...)      // Results in /api/users
server.RemoveBasePath()
server.Get("/health", ...)     // Results in /health

Returns:

  • *Server: The server itself to allow method chaining

func (*Server) Router

func (s *Server) Router(path ...string) *mux.Router

Router returns mux.Router, it may be useful if you want to work with router manually. It accepts a path to set as a base path for the router.

func (*Server) Shutdown

func (s *Server) Shutdown(ctx context.Context) error

Shutdown gracefully shuts down both HTTP and HTTPS servers.

This method attempts to gracefully shut down all running servers by:

  1. Stopping acceptance of new connections
  2. Allowing existing connections to complete their requests
  3. Running all registered cleanup functions
  4. Returning when all operations complete or the context times out

The context controls the shutdown timeout. It's recommended to use a timeout context (e.g., 30 seconds) to prevent hanging on long-running requests.

Parameters:

  • ctx: Context with timeout for the shutdown operation

Returns an error if any server fails to shut down properly. Multiple errors are joined using errors.Join().

Example:

// Graceful shutdown with 30-second timeout
shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

if err := server.Shutdown(shutdownCtx); err != nil {
	log.Printf("Server shutdown error: %v", err)
}

func (*Server) Start

func (s *Server) Start(httpAddr, httpsAddr string) error

Start starts both HTTP and HTTPS servers on the specified addresses.

Parameters:

  • httpAddr: Address for HTTP server (e.g., ":8080", "localhost:8080"). Empty string disables HTTP.
  • httpsAddr: Address for HTTPS server (e.g., ":8443", "localhost:8443"). Empty string disables HTTPS.

At least one address must be provided. The HTTPS server requires a TLS certificate to be configured through WithCertificate() or WithCertificateFromFile() options.

This method starts the servers asynchronously and returns immediately after successful startup. Use StartWithShutdown() if you need automatic cleanup when a context is cancelled.

Example:

// Start both HTTP and HTTPS
err := server.Start(":8080", ":8443")

// Start only HTTP
err := server.Start(":8080", "")

// Start only HTTPS
err := server.Start("", ":8443")

func (*Server) StartHTTP

func (s *Server) StartHTTP(address string) error

StartHTTP starts only the HTTP server on the specified address.

This method is useful when you only need HTTP traffic or want to start HTTP and HTTPS servers separately with different timing.

Parameters:

  • address: The address to bind the HTTP server to (e.g., ":8080", "localhost:8080")

The server starts asynchronously in a goroutine and this method returns immediately after successful startup. Use the returned error to check for startup failures.

Example:

if err := server.StartHTTP(":8080"); err != nil {
	log.Fatalf("Failed to start HTTP server: %v", err)
}
log.Println("HTTP server started on :8080")

func (*Server) StartHTTPS

func (s *Server) StartHTTPS(address string) error

StartHTTPS starts only the HTTPS server on the specified address.

This method requires a TLS certificate to be configured through WithCertificate() or WithCertificateFromFile() options.

Parameters:

  • address: The address to bind the HTTPS server to (e.g., ":8443", "localhost:8443")

The server starts asynchronously in a goroutine and this method returns immediately after successful startup. The method will return an error if no TLS certificate is configured.

Example:

server, err := servex.New(
	servex.WithCertificateFromFile("cert.pem", "key.pem"),
)
if err != nil {
	log.Fatal(err)
}

if err := server.StartHTTPS(":8443"); err != nil {
	log.Fatalf("Failed to start HTTPS server: %v", err)
}
log.Println("HTTPS server started on :8443")

func (*Server) StartWithShutdown

func (s *Server) StartWithShutdown(ctx context.Context, httpAddr, httpsAddr string) error

StartWithShutdown starts HTTP and HTTPS servers with automatic graceful shutdown.

This method starts the servers and monitors the provided context. When the context is cancelled or times out, it automatically initiates a graceful shutdown with a 30-second timeout.

Parameters:

  • ctx: Context for controlling server lifecycle. When cancelled, triggers shutdown.
  • httpAddr: Address for HTTP server (empty string disables HTTP)
  • httpsAddr: Address for HTTPS server (empty string disables HTTPS)

This is the recommended way to start servers in production as it handles cleanup automatically and supports graceful shutdown for zero-downtime deployments.

Example:

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// Start server with automatic shutdown
err := server.StartWithShutdown(ctx, ":8080", ":8443")
if err != nil {
	log.Fatalf("Server failed: %v", err)
}

// Server will shutdown gracefully when ctx is cancelled
// or when the program receives a signal

func (*Server) StartWithShutdownHTTP

func (s *Server) StartWithShutdownHTTP(ctx context.Context, address string) error

StartWithShutdownHTTP starts only the HTTP server with automatic graceful shutdown.

This is a convenience method that starts only the HTTP server and automatically shuts it down when the context is cancelled. Equivalent to calling StartWithShutdown(ctx, address, "").

Parameters:

  • ctx: Context for controlling server lifecycle
  • address: Address for the HTTP server (e.g., ":8080")

Example:

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

err := server.StartWithShutdownHTTP(ctx, ":8080")

func (*Server) StartWithShutdownHTTPS

func (s *Server) StartWithShutdownHTTPS(ctx context.Context, address string) error

StartWithShutdownHTTPS starts only the HTTPS server with automatic graceful shutdown.

This is a convenience method that starts only the HTTPS server and automatically shuts it down when the context is cancelled. Equivalent to calling StartWithShutdown(ctx, "", address).

Requires a TLS certificate to be configured through WithCertificate() or WithCertificateFromFile() options.

Parameters:

  • ctx: Context for controlling server lifecycle
  • address: Address for the HTTPS server (e.g., ":8443")

Example:

server, err := servex.New(
	servex.WithCertificateFromFile("cert.pem", "key.pem"),
)
if err != nil {
	log.Fatal(err)
}

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err = server.StartWithShutdownHTTPS(ctx, ":8443")

func (*Server) StartWithWaitSignals added in v2.0.1

func (s *Server) StartWithWaitSignals(ctx context.Context, httpAddr, httpsAddr string, signals ...os.Signal) error

StartWithWaitSignals starts the server with automatic graceful shutdown when the provided signals are received.

This method starts the servers and waits (blocks) until the provided signals are received. When the signals are received, it automatically initiates a graceful shutdown with a 30-second timeout.

Parameters:

  • ctx: Context for controlling server lifecycle
  • httpAddr: Address for HTTP server (empty string disables HTTP)
  • httpsAddr: Address for HTTPS server (empty string disables HTTPS)
  • signals: Signals to listen for (default: [os.Interrupt, syscall.SIGTERM])

Example:

server, err := servex.New(
	servex.WithCertificateFromFile("cert.pem", "key.pem"),
)
if err != nil {
	log.Fatal(err)
}

err = server.StartWithWaitSignals(ctx, ":8080", ":8443", os.Interrupt, syscall.SIGTERM)
if err != nil {
	log.Fatal(err)
}

// Server will shutdown gracefully when ctx is cancelled
// or when the program receives a signal

func (*Server) StartWithWaitSignalsHTTP added in v2.0.1

func (s *Server) StartWithWaitSignalsHTTP(ctx context.Context, address string, signals ...os.Signal) error

StartWithWaitSignalsHTTP starts the HTTP server with automatic graceful shutdown when the provided signals are received.

This method starts the HTTP server and waits (blocks) until the provided signals are received. When the signals are received, it automatically initiates a graceful shutdown with a 30-second timeout.

Parameters:

  • ctx: Context for controlling server lifecycle
  • httpAddr: Address for HTTP server (empty string disables HTTP)
  • httpsAddr: Address for HTTPS server (empty string disables HTTPS)
  • signals: Signals to listen for (default: [os.Interrupt, syscall.SIGTERM])

Example:

server, err := servex.New(
	servex.WithCertificateFromFile("cert.pem", "key.pem"),
)
if err != nil {
	log.Fatal(err)
}

err = server.StartWithWaitSignalsHTTP(ctx, ":8080", os.Interrupt, syscall.SIGTERM)
if err != nil {
	log.Fatal(err)
}

// Server will shutdown gracefully when ctx is cancelled
// or when the program receives a signal

func (*Server) StartWithWaitSignalsHTTPS added in v2.0.1

func (s *Server) StartWithWaitSignalsHTTPS(ctx context.Context, address string, signals ...os.Signal) error

StartWithWaitSignalsHTTPS starts the HTTPS server with automatic graceful shutdown when the provided signals are received.

This method starts the HTTPS server and waits (blocks) until the provided signals are received. When the signals are received, it automatically initiates a graceful shutdown with a 30-second timeout.

Parameters:

  • ctx: Context for controlling server lifecycle
  • httpAddr: Address for HTTP server (empty string disables HTTP)
  • httpsAddr: Address for HTTPS server (empty string disables HTTPS)
  • signals: Signals to listen for (default: [os.Interrupt, syscall.SIGTERM])

Example:

server, err := servex.New(
	servex.WithCertificateFromFile("cert.pem", "key.pem"),
)
if err != nil {
	log.Fatal(err)
}

err = server.StartWithWaitSignalsHTTPS(ctx, ":8443", os.Interrupt, syscall.SIGTERM)
if err != nil {
	log.Fatal(err)
}

// Server will shutdown gracefully when ctx is cancelled
// or when the program receives a signal

func (*Server) TRACE

func (s *Server) TRACE(path string, h http.HandlerFunc) *mux.Route

TRACE is an alias for Server.Trace.

func (*Server) Trace

func (s *Server) Trace(path string, h http.HandlerFunc) *mux.Route

Trace registers a new TRACE route with the provided path and http.HandlerFunc. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) TraceWithAuth

func (s *Server) TraceWithAuth(path string, h http.HandlerFunc, roles ...UserRole) *mux.Route

TraceWithAuth registers a new TRACE route with the provided path and http.HandlerFunc. It adds auth middleware to the route with the provided roles. It returns a pointer to the created mux.Route to set additional settings to the route.

func (*Server) Use

func (s *Server) Use(middleware ...func(http.Handler) http.Handler)

Use adds one or more mux.MiddlewareFunc to the router.

func (*Server) WithAuth

func (s *Server) WithAuth(next http.HandlerFunc, roles ...UserRole) http.HandlerFunc

WithAuth adds auth middleware to the router with the provided roles. It returns a pointer to the created mux.Route to set additional settings to the route.

Parameters:

  • next: The next handler to register the route for
  • roles: The roles to register the route for

Returns:

  • http.HandlerFunc: The created handler to register the route for

func (*Server) WithBasePath

func (s *Server) WithBasePath(path string) *Server

WithBasePath sets the base path for the server's router. It returns the server itself to allow method chaining.

Parameters:

  • path: The base path to set for the router

Example:

server.WithBasePath("/api")
server.Get("/users", ...)

Result: /api/users

Returns:

  • *Server: The server itself to allow method chaining

type ServerConfig

type ServerConfig struct {
	HTTP                    string        `yaml:"http" json:"http" env:"SERVEX_SERVER_HTTP"`
	HTTPS                   string        `yaml:"https" json:"https" env:"SERVEX_SERVER_HTTPS"`
	CertFile                string        `yaml:"cert_file" json:"cert_file" env:"SERVEX_SERVER_CERT_FILE"`
	KeyFile                 string        `yaml:"key_file" json:"key_file" env:"SERVEX_SERVER_KEY_FILE"`
	ReadTimeout             time.Duration `yaml:"read_timeout" json:"read_timeout" env:"SERVEX_SERVER_READ_TIMEOUT"`
	ReadHeaderTimeout       time.Duration `yaml:"read_header_timeout" json:"read_header_timeout" env:"SERVEX_SERVER_READ_HEADER_TIMEOUT"`
	IdleTimeout             time.Duration `yaml:"idle_timeout" json:"idle_timeout" env:"SERVEX_SERVER_IDLE_TIMEOUT"`
	AuthToken               string        `yaml:"auth_token" json:"auth_token" env:"SERVEX_SERVER_AUTH_TOKEN"`
	HealthPath              string        `yaml:"health_path" json:"health_path" env:"SERVEX_SERVER_HEALTH_PATH"`
	MetricsPath             string        `yaml:"metrics_path" json:"metrics_path" env:"SERVEX_SERVER_METRICS_PATH"`
	EnableHealthEndpoint    bool          `yaml:"enable_health_endpoint" json:"enable_health_endpoint" env:"SERVEX_SERVER_ENABLE_HEALTH_ENDPOINT"`
	EnableDefaultMetrics    bool          `yaml:"enable_default_metrics" json:"enable_default_metrics" env:"SERVEX_SERVER_ENABLE_DEFAULT_METRICS"`
	SendErrorToClient       bool          `yaml:"send_error_to_client" json:"send_error_to_client" env:"SERVEX_SERVER_SEND_ERROR_TO_CLIENT"`
	EnableRequestSizeLimits bool          `yaml:"enable_request_size_limits" json:"enable_request_size_limits" env:"SERVEX_SERVER_ENABLE_REQUEST_SIZE_LIMITS"`
	MaxRequestBodySize      int64         `yaml:"max_request_body_size" json:"max_request_body_size" env:"SERVEX_SERVER_MAX_REQUEST_BODY_SIZE"`
	MaxJSONBodySize         int64         `yaml:"max_json_body_size" json:"max_json_body_size" env:"SERVEX_SERVER_MAX_JSON_BODY_SIZE"`
	MaxFileUploadSize       int64         `yaml:"max_file_upload_size" json:"max_file_upload_size" env:"SERVEX_SERVER_MAX_FILE_UPLOAD_SIZE"`
	MaxMultipartMemory      int64         `yaml:"max_multipart_memory" json:"max_multipart_memory" env:"SERVEX_SERVER_MAX_MULTIPART_MEMORY"`

	HTTPSRedirect HTTPSRedirectConfiguration `yaml:"https_redirect" json:"https_redirect"`
}

ServerConfig represents basic server configuration

type StaticFileConfig

type StaticFileConfig struct {
	// Enabled determines whether static file serving is active.
	// Must be set to true for static files to be served.
	// Set via WithStaticFiles(), WithSPAMode(), or WithStaticFileConfig().
	Enabled bool

	// Dir is the directory containing static files to serve.
	// This is typically the build output directory for React/Vue/Angular apps.
	// Set via WithStaticFiles().
	//
	// Common examples:
	//   - "build": React build output
	//   - "dist": Vue/Angular build output
	//   - "public": Static website files
	//   - "static": General static assets
	//
	// Files in this directory will be served at the root path unless URLPrefix is set.
	Dir string

	// URLPrefix is the URL path prefix for serving static files.
	// If empty, files are served from the root path.
	// Set via WithStaticFiles() or WithStaticFileConfig().
	//
	// Examples:
	//   - "" (empty): Files served from root (e.g., /app.js)
	//   - "/static": Files served under /static (e.g., /static/app.js)
	//   - "/assets": Files served under /assets (e.g., /assets/app.js)
	//
	// For SPAs, this is usually empty so the app is served from the root.
	URLPrefix string

	// SPAMode enables Single Page Application mode with client-side routing support.
	// When enabled, requests that don't match existing files or API routes
	// will be served the IndexFile to support client-side routing.
	// Set via WithSPAMode().
	//
	// Use cases:
	//   - React Router applications
	//   - Vue Router applications
	//   - Angular routing
	//   - Any SPA with client-side routing
	//
	// When SPAMode is true, API routes should be registered before enabling static files.
	SPAMode bool

	// IndexFile is the fallback file to serve for SPA client-side routing.
	// This file is served when a request doesn't match an existing file or API route.
	// Only used when SPAMode is true. Set via WithSPAMode() or WithStaticFileConfig().
	//
	// Common values:
	//   - "index.html": Standard for most SPAs
	//   - "app.html": Custom entry point
	//
	// Default: "index.html" if SPAMode is true and this is empty.
	IndexFile string

	// StripPrefix removes the specified prefix from the URL before looking up files.
	// This is useful when serving files from a subdirectory but accessing them via a different URL structure.
	// Set via WithStaticFileConfig().
	//
	// Example:
	//   - URLPrefix: "/app"
	//   - StripPrefix: "/app"
	//   - Request: "/app/index.html" → looks for file at "index.html" in Dir
	StripPrefix string

	// ExcludePaths are URL paths that should not be served as static files.
	// These paths will be skipped by the static file handler, allowing other handlers to process them.
	// Set via WithStaticFileConfig().
	//
	// Common exclusions:
	//   - "/api/*": API endpoints
	//   - "/auth/*": Authentication endpoints
	//   - "/admin/*": Admin interfaces
	//   - "/ws/*": WebSocket endpoints
	//
	// Path matching supports wildcards (*) for pattern matching.
	// API routes registered before static files are automatically excluded.
	ExcludePaths []string

	// CacheMaxAge sets the Cache-Control max-age directive for static files (in seconds).
	// This controls how long browsers and proxies cache static files.
	// Set via WithStaticFileConfig().
	//
	// Common values:
	//   - 3600: 1 hour (development)
	//   - 86400: 1 day (staging)
	//   - 31536000: 1 year (production, for versioned assets)
	//   - 0: No caching
	//
	// Different file types can have different cache policies by using the CacheRules field.
	CacheMaxAge int

	// CacheRules defines cache policies for different file types or paths.
	// The key is a file extension (e.g., ".js", ".css") or path pattern (e.g., "/images/*").
	// The value is the max-age in seconds.
	// Set via WithStaticFileConfig().
	//
	// Example:
	//   map[string]int{
	//     ".js":        31536000, // 1 year for JS files
	//     ".css":       31536000, // 1 year for CSS files
	//     ".html":      3600,     // 1 hour for HTML files
	//     "/images/*":  2592000,  // 30 days for images
	//   }
	//
	// More specific rules override general rules. CacheRules override CacheMaxAge.
	CacheRules map[string]int
	// contains filtered or unexported fields
}

StaticFileConfig holds configuration for serving static files and Single Page Applications (SPAs).

type StaticFilesConfiguration

type StaticFilesConfiguration struct {
	Enabled      bool           `yaml:"enabled" json:"enabled" env:"SERVEX_STATIC_FILES_ENABLED"`
	Dir          string         `yaml:"dir" json:"dir" env:"SERVEX_STATIC_FILES_DIR"`
	URLPrefix    string         `yaml:"url_prefix" json:"url_prefix" env:"SERVEX_STATIC_FILES_URL_PREFIX"`
	SPAMode      bool           `yaml:"spa_mode" json:"spa_mode" env:"SERVEX_STATIC_FILES_SPA_MODE"`
	IndexFile    string         `yaml:"index_file" json:"index_file" env:"SERVEX_STATIC_FILES_INDEX_FILE"`
	StripPrefix  string         `yaml:"strip_prefix" json:"strip_prefix" env:"SERVEX_STATIC_FILES_STRIP_PREFIX"`
	ExcludePaths []string       `yaml:"exclude_paths" json:"exclude_paths" env:"SERVEX_STATIC_FILES_EXCLUDE_PATHS"`
	CacheMaxAge  int            `yaml:"cache_max_age" json:"cache_max_age" env:"SERVEX_STATIC_FILES_CACHE_MAX_AGE"`
	CacheRules   map[string]int `yaml:"cache_rules" json:"cache_rules"`
}

StaticFilesConfiguration represents static file serving configuration

type TrafficDumpConfig

type TrafficDumpConfig struct {
	// Enabled indicates if traffic dumping is enabled globally
	Enabled bool `yaml:"enabled" json:"enabled"`
	// Directory where to store traffic dumps
	Directory string `yaml:"directory" json:"directory"`
	// MaxFileSize for rotation (in bytes, default: 100MB)
	MaxFileSize int64 `yaml:"max_file_size" json:"max_file_size"`
	// MaxFiles for retention (default: 10)
	MaxFiles int `yaml:"max_files" json:"max_files"`
	// IncludeBody whether to include request/response bodies
	IncludeBody bool `yaml:"include_body" json:"include_body"`
	// MaxBodySize maximum body size to dump (default: 64KB)
	MaxBodySize int64 `yaml:"max_body_size" json:"max_body_size"`
	// SampleRate for sampling traffic (0.0-1.0, default: 1.0 = all traffic)
	SampleRate float64 `yaml:"sample_rate" json:"sample_rate"`
}

TrafficDumpConfig configures traffic dumping

type User

type User struct {
	ID                    string     `json:"id" bson:"_id" db:"id"`
	Username              string     `json:"username" bson:"username" db:"username"`
	Roles                 []UserRole `json:"roles" bson:"roles" db:"roles"`
	PasswordHash          string     `json:"password_hash" bson:"password_hash" db:"password_hash"`
	RefreshTokenHash      string     `json:"refresh_token_hash" bson:"refresh_token_hash" db:"refresh_token_hash"`
	RefreshTokenExpiresAt time.Time  `json:"refresh_token_expires_at" bson:"refresh_token_expires_at" db:"refresh_token_expires_at"`
}

User represents a user entity in the system.

type UserContextKey

type UserContextKey struct{}

type UserDiff

type UserDiff struct {
	Username              *string     `json:"username,omitempty" bson:"username,omitempty" db:"username,omitempty"`
	Roles                 *[]UserRole `json:"roles,omitempty" bson:"roles,omitempty" db:"roles,omitempty"`
	PasswordHash          *string     `json:"password_hash,omitempty" bson:"password_hash,omitempty" db:"password_hash,omitempty"`
	RefreshTokenHash      *string     `json:"refresh_token_hash,omitempty" bson:"refresh_token_hash,omitempty" db:"refresh_token_hash,omitempty"`
	RefreshTokenExpiresAt *time.Time  `json:"refresh_token_expires_at,omitempty" bson:"refresh_token_expires_at,omitempty" db:"refresh_token_expires_at,omitempty"`
}

type UserLoginRequest

type UserLoginRequest struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

UserLoginRequest represents the request body for user login and registration.

func (UserLoginRequest) Validate

func (req UserLoginRequest) Validate() error

Validate checks if the UserLoginRequest is valid.

type UserLoginResponse

type UserLoginResponse struct {
	ID          string     `json:"id"`
	Username    string     `json:"username"`
	Roles       []UserRole `json:"roles"`
	AccessToken string     `json:"accessToken,omitempty"`
}

UserLoginResponse represents the response body for successful user login or registration.

type UserRole

type UserRole string

UserRole represents a role assigned to a user. It's defined as a string type for easy JSON marshaling/unmarshaling.

type UserUpdateRequest

type UserUpdateRequest struct {
	ID       string      `json:"id"`
	Username *string     `json:"username,omitempty"`
	Roles    *[]UserRole `json:"roles,omitempty"`
	Password *string     `json:"password,omitempty"`
}

UserUpdateRequest represents the request body for updating user information.

func (UserUpdateRequest) Validate

func (req UserUpdateRequest) Validate() error

Validate checks if the UserUpdateRequest is valid.

Directories

Path Synopsis
cmd
servex command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL