Proxy middleware forwards requests to one or more upstream servers with support for load balancing, custom request/response modification, and flexible routing.
Installation
go get -u github.com/gofiber/fiber/v3
go get -u github.com/gofiber/fiber/v3/middleware/proxy
Signatures
// Balancer creates a load balancer among multiple upstream servers
func Balancer(config Config) fiber.Handler
// Forward performs HTTP request and fills the response
func Forward(addr string, clients ...*fasthttp.Client) fiber.Handler
// Do performs HTTP request and fills the response
func Do(c fiber.Ctx, addr string, clients ...*fasthttp.Client) error
// DoRedirects performs request and follows up to maxRedirectsCount redirects
func DoRedirects(c fiber.Ctx, addr string, maxRedirectsCount int, clients ...*fasthttp.Client) error
// DoDeadline performs request and waits until the deadline
func DoDeadline(c fiber.Ctx, addr string, deadline time.Time, clients ...*fasthttp.Client) error
// DoTimeout performs request with a timeout
func DoTimeout(c fiber.Ctx, addr string, timeout time.Duration, clients ...*fasthttp.Client) error
// DomainForward forwards requests for a specific domain
func DomainForward(hostname string, addr string, clients ...*fasthttp.Client) fiber.Handler
// BalancerForward performs round-robin load balancing
func BalancerForward(servers []string, clients ...*fasthttp.Client) fiber.Handler
Usage
Basic Forwarding
package main
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/proxy"
)
func main() {
app := fiber.New()
// Forward all requests to another server
app.Use(proxy.Forward("http://localhost:8080"))
app.Listen(":3000")
}
Load Balancer
app.Use(proxy.Balancer(proxy.Config{
Servers: []string{
"http://localhost:3001",
"http://localhost:3002",
"http://localhost:3003",
},
}))
Custom Client Configuration
import (
"crypto/tls"
"github.com/valyala/fasthttp"
)
// Set global custom client
proxy.WithClient(&fasthttp.Client{
NoDefaultUserAgentHeader: true,
DisablePathNormalizing: true,
TLSConfig: &tls.Config{
InsecureSkipVerify: true,
},
})
// Or per-request
app.Get("/api/*", proxy.Forward("https://api.example.com", &fasthttp.Client{
NoDefaultUserAgentHeader: true,
}))
Domain-Based Routing
app.Get("/payments", proxy.DomainForward("docs.gofiber.io", "http://localhost:8000"))
Manual Proxy Request
app.Get("/:id", func(c fiber.Ctx) error {
url := "https://api.example.com/" + c.Params("id")
if err := proxy.Do(c, url); err != nil {
return err
}
// Modify response
c.Response().Header.Del(fiber.HeaderServer)
return nil
})
Follow Redirects
app.Get("/proxy", func(c fiber.Ctx) error {
if err := proxy.DoRedirects(c, "http://example.com", 3); err != nil {
return err
}
return nil
})
With Timeout
app.Get("/proxy", func(c fiber.Ctx) error {
if err := proxy.DoTimeout(c, "http://localhost:8080", 5*time.Second); err != nil {
return err
}
return nil
})
With Deadline
app.Get("/proxy", func(c fiber.Ctx) error {
deadline := time.Now().Add(time.Minute)
if err := proxy.DoDeadline(c, "http://localhost:8080", deadline); err != nil {
return err
}
return nil
})
Configuration
Next
func(fiber.Ctx) bool
default:"nil"
Function to skip this middleware when it returns true.
Required. List of upstream servers in format <scheme>://<host>. Used in round-robin manner.
ModifyRequest
fiber.Handler
default:"nil"
Handler to modify the request before forwarding.
ModifyResponse
fiber.Handler
default:"nil"
Handler to modify the response before returning.
Timeout
time.Duration
default:"1 second"
Request timeout when calling the proxy client.
ReadBufferSize
int
default:"(Not specified)"
Per-connection buffer size for reading requests. Also limits maximum header size.
WriteBufferSize
int
default:"(Not specified)"
Per-connection buffer size for writing responses.
Keeps the Connection header when true. By default, removed per RFC 7230 §6.1.
TLS configuration for the HTTP client.
Attempts to connect to both IPv4 and IPv6 addresses when true.
Client
*fasthttp.LBClient
default:"nil"
Custom client for complex configurations. When set, other client options are ignored.
Default Configuration
var ConfigDefault = Config{
Next: nil,
ModifyRequest: nil,
ModifyResponse: nil,
Timeout: fasthttp.DefaultLBClientTimeout,
KeepConnectionHeader: false,
}
Best Practices
Modify Requests
app.Use(proxy.Balancer(proxy.Config{
Servers: []string{
"http://localhost:3001",
"http://localhost:3002",
},
ModifyRequest: func(c fiber.Ctx) error {
// Add headers
c.Request().Header.Add("X-Real-IP", c.IP())
c.Request().Header.Add("X-Forwarded-For", c.IP())
return nil
},
}))
Modify Responses
app.Use(proxy.Balancer(proxy.Config{
Servers: []string{"http://localhost:3001"},
ModifyResponse: func(c fiber.Ctx) error {
// Remove server header
c.Response().Header.Del(fiber.HeaderServer)
// Add custom header
c.Response().Header.Add("X-Proxy", "Fiber")
return nil
},
}))
HTTPS to HTTP Proxy
// When balancer uses HTTPS but destination uses HTTP
app.Use(proxy.BalancerForward([]string{
"http://localhost:3001",
"http://localhost:3002",
}))
IPv6 Support
app.Use(proxy.Balancer(proxy.Config{
Servers: []string{
"http://[::1]:3001",
"http://127.0.0.1:3002",
"http://localhost:3003",
},
DialDualStack: true, // Enable TCP4 and TCP6
}))
Common Patterns
API Gateway
// Route to different services based on path
app.Use("/users", proxy.Forward("http://users-service:8080"))
app.Use("/orders", proxy.Forward("http://orders-service:8080"))
app.Use("/payments", proxy.Forward("http://payments-service:8080"))
Conditional Proxying
app.Use(proxy.Balancer(proxy.Config{
Servers: []string{"http://localhost:3001"},
Next: func(c fiber.Ctx) bool {
// Skip proxy for static files
return strings.HasPrefix(c.Path(), "/static")
},
}))
Custom Load Balancing
servers := []string{
"http://localhost:3001",
"http://localhost:3002",
"http://localhost:3003",
}
var counter uint32
app.Use(func(c fiber.Ctx) error {
// Simple round-robin
idx := atomic.AddUint32(&counter, 1) % uint32(len(servers))
return proxy.Do(c, servers[idx])
})
Health Check and Failover
type Server struct {
URL string
Healthy bool
}
servers := []*Server{
{URL: "http://localhost:3001", Healthy: true},
{URL: "http://localhost:3002", Healthy: true},
}
app.Use(func(c fiber.Ctx) error {
for _, srv := range servers {
if !srv.Healthy {
continue
}
if err := proxy.Do(c, srv.URL); err != nil {
srv.Healthy = false
continue
}
return nil
}
return fiber.ErrServiceUnavailable
})
Request/Response Logging
app.Use(proxy.Balancer(proxy.Config{
Servers: []string{"http://localhost:3001"},
ModifyRequest: func(c fiber.Ctx) error {
log.Printf("Proxying %s %s", c.Method(), c.Path())
return nil
},
ModifyResponse: func(c fiber.Ctx) error {
log.Printf("Response %d from upstream", c.Response().StatusCode())
return nil
},
}))
WebSocket Proxy
import "github.com/gofiber/fiber/v3/middleware/websocket"
app.Use("/ws", func(c fiber.Ctx) error {
if websocket.IsWebSocketUpgrade(c) {
// WebSocket upgrade
return proxy.Do(c, "ws://localhost:8080/ws")
}
return c.Next()
})
Authentication Proxy
app.Use(proxy.Balancer(proxy.Config{
Servers: []string{"http://localhost:3001"},
ModifyRequest: func(c fiber.Ctx) error {
// Add authentication token
token := c.Get("Authorization")
if token == "" {
return fiber.ErrUnauthorized
}
// Validate and forward
return nil
},
}))
CORS Proxy
import "github.com/gofiber/fiber/v3/middleware/cors"
app.Use(cors.New())
app.Use(proxy.Forward("http://api.example.com"))
Testing
# Test basic proxy
curl http://localhost:3000/api/users
# Test with custom headers
curl -H "X-Custom-Header: value" http://localhost:3000/api/users
# Test load balancing (multiple requests)
for i in {1..10}; do
curl http://localhost:3000/
done
- Reuse clients: Use
proxy.WithClient() to set a global client
- Tune buffer sizes: Adjust
ReadBufferSize and WriteBufferSize for your workload
- Connection pooling: fasthttp automatically pools connections
- Timeouts: Always set appropriate timeouts to prevent hanging requests
- IPv6: Enable
DialDualStack only if needed
Security Considerations
- Always validate and sanitize headers in
ModifyRequest
- Use TLS for sensitive data (
TLSConfig)
- Implement authentication/authorization before proxying
- Be careful with
KeepConnectionHeader to avoid proxy loops
- Set appropriate timeouts to prevent resource exhaustion
- Consider rate limiting on proxy endpoints