ETag middleware for Fiber that helps caches validate responses and saves bandwidth by avoiding full retransmits when content is unchanged.
Signatures
func New(config ...Config) fiber.Handler
Usage
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/etag"
)
Basic Example
// Initialize default config
app.Use(etag.New())
// GET / -> ETag: "13-1831710635"
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Hello, World!")
})
// Or extend your config for customization
app.Use(etag.New(etag.Config{
Weak: true,
}))
// GET / -> ETag: W/"13-1831710635"
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Hello, World!")
})
Entity tags in requests must be quoted per RFC 9110. For example:If-None-Match: "example-etag"
Configuration
Next
func(fiber.Ctx) bool
default:"nil"
Defines a function to skip this middleware when it returns true.
Enables weak validators. Weak ETags are easier to generate but less reliable for comparisons. Weak ETags are prefixed with W/.
Default Config
var ConfigDefault = Config{
Next: nil,
Weak: false,
}
How It Works
The ETag middleware generates a unique hash based on the response body:
- Strong ETags (default): Used for byte-for-byte identical content
- Weak ETags: Indicated by
W/ prefix, used when content is semantically equivalent but not byte-identical
When a client sends an If-None-Match header with a matching ETag, the server returns 304 Not Modified instead of the full response body, saving bandwidth.
Common Use Cases
API Response Caching
app.Use(etag.New())
app.Get("/api/users", func(c fiber.Ctx) error {
users := getUsersFromDB()
return c.JSON(users)
})
Static Content with Strong ETags
app.Use("/static", etag.New(etag.Config{
Weak: false, // Strong ETags for exact matching
}))
app.Static("/static", "./public")
Dynamic Content with Weak ETags
app.Use("/pages", etag.New(etag.Config{
Weak: true, // Content may vary slightly
}))
app.Get("/pages/:id", func(c fiber.Ctx) error {
page := getPageContent(c.Params("id"))
return c.SendString(page)
})
Selective ETag Usage
app.Use(etag.New(etag.Config{
Next: func(c fiber.Ctx) bool {
// Skip ETag for certain paths
return c.Path() == "/api/realtime" ||
c.Path() == "/health"
},
}))
Combining with Cache Middleware
import (
"github.com/gofiber/fiber/v3/middleware/cache"
"github.com/gofiber/fiber/v3/middleware/etag"
)
// Apply both middlewares
app.Use(etag.New())
app.Use(cache.New(cache.Config{
Expiration: 5 * time.Minute,
}))
Client Usage
# First request - get ETag
curl -i http://localhost:3000/api/data
# Response includes: ETag: "abc123"
# Subsequent request - send If-None-Match
curl -i http://localhost:3000/api/data -H 'If-None-Match: "abc123"'
# Response: 304 Not Modified (if content unchanged)
JavaScript Fetch API
let etag = null;
async function fetchData() {
const headers = {};
if (etag) {
headers['If-None-Match'] = etag;
}
const response = await fetch('/api/data', { headers });
if (response.status === 304) {
console.log('Using cached data');
return cachedData;
}
etag = response.headers.get('ETag');
const data = await response.json();
cachedData = data;
return data;
}
- Reduced Bandwidth: 304 responses have no body, saving transfer costs
- Faster Response Times: Smaller responses mean quicker page loads
- Server Load Reduction: Less data processing and transmission
- CDN Optimization: Better cache hit rates with proper ETag usage