Using a Rails Concern to block suspicious IPs

Davy Cardinaal · · Updated

Last weekend, I woke up to an inbox full of error notifications from one of my Rails applications. Something was clearly wrong. I jumped out of bed, got on my laptop, and logged into Rollbar, the error tracker.

The problem

After a quick investigation, I discovered the cause: a flood of HTTP requests from a single IP address. This wasn’t just unusual traffic. It was clearly malicious or at the very least abusive. It triggered numerous exceptions in my app and filled my logs with noise. The requests weren’t high in volume per second, so it wasn’t a typical denial-of-service situation. But they were persistent and clearly targeted, likely a bot probing for vulnerabilities, trying to spam or exploit forms.

Stop the flood

The application that was under attack is deployed on a DigitalOcean droplet using Kamal. While Kamal makes deployment simple and efficient, it currently doesn’t include built-in support for managing firewalls. And I wasn’t immediately sure how to block the IP on the droplet level.

So I turned to Rails for a quick solution. I added a small controller concern to intercept requests from the known bad IP:

module IpBlocker
extend ActiveSupport::Concern

included do
before_action :block_ip_addresses
end

protected

BLOCKED_IP_ADDRESSES = %w[127.0.0.1] # This is localhost, the real IP-address was in here

def block_ip_addresses
head :unauthorized if request.remote_ip.in? BLOCKED_IP_ADDRESSES
end
end

I then included this concern in ApplicationController, and within seconds, the attack was effectively "stopped". The IP was met with a 401 Unauthorized on every request, and the error flood stopped.

Why this worked well (for now)

This approach has a few advantages:

  • Fast to implement: It took just a few minutes to add the concern and redeploy.
  • Rails-native: No need to SSH into the server or configure anything at the OS level.
  • No downtime: The fix was deployed with Kamal and didn’t interrupt service for legitimate users.

Of course, this isn’t a long-term solution. Ideally, malicious traffic should be blocked before it hits your Rails app. Either at the firewall level or via a reverse proxy like Nginx or Cloudflare. But when you’re under immediate pressure to stop the bleeding, a simple in-app filter like this can act as a bandage.

What's next

Long term, I’m considering setting up proper firewall rules on my droplet via ufw. But I’m glad I had Rails on my side to respond quickly. If you're deploying with Kamal and find yourself in a similar situation, this is a quick fix that might save you from a stressful morning.


Update 10-08-2025:

A friend of mine read this post and mentioned to me that he once had the same issue. This led me to his question in the Kamal discussions on GitHub, where DHH suggested to set up Cloudflare for DDoS protection. I did some research on this and decided to go for this option. It turned out that Cloudflare was really easy to set up and I got it working in no time! A big benefit of using Cloudflare over ufw  is that you can block requests based on the malicious IP address before they even reach your server, nice!