Ratelimiting using Redis


What is Rate limiting and why do we need it?
Rate limiting is a technique used to control the number of requests a client can make to a server within a specified time frame. It helps prevent abuse, ensures fair resource distribution, and maintains system stability.
Prevents API abuse: Stops users and bots to from overwhelming the server with excessive requests.
Protects from attacks: Enhances security to protect from brute force attacks and Denial of Service (DoS) attacks.
Controls costs: Helps in avoiding excessive costs on resource consumption, reducing operational costs.
Pre-requisites
This is a very simple implementation of rate limiting, which can be improved upon a lot. But this can save your side projects to be heavily bombarded from excessive API calls.
Things you need to implement this:
Redis up and running (locally using docker or using any cloud service like upstash)
Golang and Gin (because Go’s default
net/http
is like building IKEA furniture without instructions)
Let’s implement
Install dependencies
These are the dependencies we need for our ratelimiting middleware to work.
go get github.com/gin-gonic/gin
go get github.com/go-redis/redis/v8
Connect to Redis 🏓
Let’s code to connect to redis.
This code is to connect with redis when you have running it locally using docker or have a redis server on your PC.
func ConnectRedis() error {
redisClient = redis.NewClient(&redis.Options{
Addr: "localhost:6379", // Redis' home address
Password: "", // No password? You can set your own password
DB: 0, // The VIP database
})
// Send a "PING" to check if Redis is awake
pong, err := redisClient.Ping(ctx).Result()
if err != nil {
log.Fatalf("Redis connection failed harder than my diet on Thanksgiving: %v", err)
}
fmt.Println("Redis says:", pong) // "PONG!" means you're in business!
return nil
}
But what if you have it up in the cloud using some service provider like upstash. Then change the fields Addr
and Password
to the ones you have from your provider, and add TLS config (it’s necessary)
Then the function for connecting to redis will look like this.
func ConnectRedis() error {
redisClient = redis.NewClient(&redis.Options{
Addr: "your-upstash-endpoint:6379", // Replace with your Upstash Redis endpoint
Password: "your-upstash-password", // Replace with your Upstash Redis password
DB: 0, // Default DB
TLSConfig: &tls.Config{}, // Enable TLS (important for Upstash)
})
// Check if Redis connection is successful
pong, err := redisClient.Ping(ctx).Result()
if err != nil {
log.Fatalf("Failed to connect to Upstash Redis: %v", err)
}
fmt.Println("Connected to Upstash Redis:", pong)
return nil
}
The Rate Limiter Middleware
The rules?
5 requests per IP per minute
More than that, you’ll get a 429 (Too many requests) error.
func RateLimiter() gin.HandlerFunc {
return func(c *gin.Context) {
ip := c.ClientIP() // Get that IP! (No cheating with proxies, okay?)
key := fmt.Sprintf("rate_limit:%s", ip) // Redis key. Example: "rate_limit:127.0.0.1"
limit := 5 // 5 requests...
window := 60 * time.Second // ...per 60 seconds. Play nice!
// Increment the request counter for this IP
count, err := redisClient.Incr(ctx, key).Result()
if err != nil {
log.Printf("Redis threw a tantrum: %v", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": "BRB, server's crying"})
return
}
// If this is the first request, set the "self-destruct" timer
if count == 1 {
if _, err := redisClient.Expire(ctx, key, window).Result(); err != nil {
log.Printf("Redis tried to ghost us: %v", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": "Server's on strike"})
return
}
}
// Enforce the limit
if count > int64(limit) {
c.AbortWithStatusJSON(http.StatusTooManyRequests, gin.H{
"message": "Slow your roll, cowboy! 🐄 Try again later.",
})
return
}
c.Next() // All good—proceed to the party!
}
}
How this works?
Incr
Increments the request count for the IPExpire
Set’s a 60 seconds TTL on the key only on the first request (so it auto deletes itself).Limit check: If the count exceeds 5, deny entry for the requests.
Add the middleware to your Gin router
Add the middleware onto your routes and protect them from getting overwhelmed.
func main() {
if err := ConnectRedis(); err != nil {
log.Fatal("Redis connection: Pong")
}
router := gin.Default()
router.Use(RateLimiter()) // Apply to all routes
router.GET("/api/secret-sauce", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "🔥 Secret sauce: 3 parts caffeine, 2 parts chaos"})
})
router.Run(":8080")
}
You can start your server using go run main
.
Now, hit /api/secret-sauce
more than 5 times in a minute, and you’ll get a sassy 429
error. Test it with curl -I http://localhost:8080/api/secret-sauce
and watch the headers fly!
Gotcha and Pro tips
IP-based limits aren’t perfect: In a shared network (like Starbucks WiFi), you might block innocent bystanders. For critical apps, use API keys or auth tokens.
Adjust the limits: 5 requests/minute too strict? Crank it up! (But not so high that your server melts.)
Upgrade to sliding windows: This example uses a fixed window. For smoother limits, try Redis’s
ZSET
with a sliding window algorithm.
Conclusion
Rate limiting is a technique to control the number of requests a client can make to a server in a given timeframe, helping prevent abuse, ensure fair resource distribution, and maintain system stability. This article covers a simple implementation using Redis, Golang, and Gin, allowing 5 requests per IP per minute. The guide includes setup instructions, connection details for local and cloud-based Redis, and middleware implementation to protect your API routes. Adjusting limits and considering advanced techniques like sliding windows are also discussed for better control and efficiency.
Bye !
Subscribe to my newsletter
Read articles from Pranav Tripathi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Pranav Tripathi
Pranav Tripathi
I'm a dedicated full-stack developer specializing in building dynamic and scalable applications. I have a strong focus on delivering efficient and user-friendly solutions, and I thrive on solving complex problems through technology.