Skip to main content
Case Studies Shopify Security

Shopify bot problem, and how we fixed it

A deep dive into how we identified and solved a massive bot attack on our Shopify store, including detection methods and prevention strategies.

honeybound Team
5 min read
Shopify bot problem, and how we fixed it

Over the past few months, one of the sites I work on has had its fair share of… botting attempts. It started subtly but quickly escalated into a full-scale attack that was affecting our analytics, inventory management, and overall site performance. Here’s how we identified the problem and implemented a comprehensive solution.

The Problem Emerges

It began innocuously enough. We noticed:

  • Unusual spikes in “Add to Cart” events
  • Hundreds of abandoned carts daily
  • Analytics data that didn’t match actual sales
  • Server performance degradation during peak attack times

The most telling sign? Our backend events were going through the roof while frontend engagement remained normal.

Investigating the Attack

Step 1: Analyzing Traffic Patterns

We started by diving deep into our analytics. Here’s what we found:

// Suspicious pattern in our event logs
{
  "event": "product_added_to_cart",
  "timestamp": "2023-08-15T14:23:45Z",
  "ip": "52.91.xxx.xxx",  // AWS IP
  "user_agent": "Mozilla/5.0 (compatible)",
  "page_view_before_add": false,  // RED FLAG!
  "time_on_site": 0.3  // seconds
}

The pattern was clear:

  • 90% of suspicious traffic came from AWS IP addresses (primarily Ashburn, VA)
  • No “page_viewed” events before “add_to_cart” events
  • User sessions lasting less than 1 second
  • Identical user agent strings

Step 2: Identifying the Bot Behavior

The bots were sophisticated:

  1. They bypassed the frontend entirely
  2. Directly POSTed to our /cart/add endpoint
  3. Added random quantities of random products
  4. Never proceeded to checkout

Here’s a sample of what we captured:

# Bot request pattern
POST /cart/add.js
Host: ourstore.myshopify.com
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (compatible)

id=123456789&quantity=99&properties[gift_wrap]=true

Our Multi-Layered Solution

Layer 1: Honeypot Fields

We implemented invisible form fields that humans wouldn’t fill:

<!-- In our product form -->
<form action="/cart/add" method="post">
  <!-- Regular fields -->
  <input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">
  
  <!-- Honeypot field - hidden with CSS -->
  <input 
    type="text" 
    name="url" 
    style="position: absolute; left: -9999px;" 
    tabindex="-1" 
    autocomplete="off"
    aria-hidden="true"
  >
  
  <!-- Real quantity field -->
  <input type="number" name="quantity" value="1">
</form>

Then in our theme’s JavaScript:

document.addEventListener('submit', function(e) {
  if (e.target.matches('form[action*="/cart/add"]')) {
    const honeypot = e.target.querySelector('input[name="url"]');
    
    if (honeypot && honeypot.value !== '') {
      e.preventDefault();
      console.log('Bot detected via honeypot');
      // Optionally send to monitoring service
      return false;
    }
  }
});

Layer 2: Rate Limiting with JavaScript

We implemented client-side rate limiting:

const RateLimiter = {
  attempts: {},
  
  canAddToCart: function(productId) {
    const now = Date.now();
    const key = `add_${productId}`;
    const lastAttempt = this.attempts[key] || 0;
    
    // Minimum 2 seconds between add to cart for same product
    if (now - lastAttempt < 2000) {
      console.warn('Rate limit exceeded');
      return false;
    }
    
    this.attempts[key] = now;
    return true;
  },
  
  clearOldAttempts: function() {
    const now = Date.now();
    const oneHourAgo = now - (60 * 60 * 1000);
    
    Object.keys(this.attempts).forEach(key => {
      if (this.attempts[key] < oneHourAgo) {
        delete this.attempts[key];
      }
    });
  }
};

// Clean up every 10 minutes
setInterval(() => RateLimiter.clearOldAttempts(), 10 * 60 * 1000);

Layer 3: Behavioral Analysis

We added tracking to identify bot-like behavior:

class BotDetector {
  constructor() {
    this.mouseMovements = 0;
    this.keyPresses = 0;
    this.touchEvents = 0;
    this.startTime = Date.now();
    this.isBot = false;
    
    this.initListeners();
  }
  
  initListeners() {
    // Track mouse movements
    let lastMove = 0;
    document.addEventListener('mousemove', () => {
      const now = Date.now();
      if (now - lastMove > 50) {  // Throttle
        this.mouseMovements++;
        lastMove = now;
      }
    });
    
    // Track keyboard
    document.addEventListener('keypress', () => {
      this.keyPresses++;
    });
    
    // Track touch
    document.addEventListener('touchstart', () => {
      this.touchEvents++;
    });
  }
  
  analyze() {
    const timeOnSite = (Date.now() - this.startTime) / 1000;
    
    // Bot indicators
    if (timeOnSite > 5 && this.mouseMovements === 0 && this.touchEvents === 0) {
      this.isBot = true;
    }
    
    // Too fast interactions
    if (timeOnSite < 0.5 && window.cartActions > 0) {
      this.isBot = true;
    }
    
    return !this.isBot;
  }
}

const detector = new BotDetector();

// Check before cart actions
function addToCart(productId, quantity) {
  if (!detector.analyze()) {
    console.warn('Bot-like behavior detected');
    return false;
  }
  
  // Proceed with normal add to cart
}

Layer 4: Server-Side Validation (Shopify Plus)

For Shopify Plus stores, we implemented Shopify Functions:

// Shopify Function for cart validation
export default {
  async run(input) {
    const { cart } = input;
    
    // Check for suspicious patterns
    const errors = [];
    
    // Too many unique items
    if (cart.lines.length > 20) {
      errors.push({
        localizedMessage: "Cart limit exceeded",
        target: "cart"
      });
    }
    
    // Excessive quantities
    const totalQuantity = cart.lines.reduce((sum, line) => 
      sum + line.quantity, 0
    );
    
    if (totalQuantity > 100) {
      errors.push({
        localizedMessage: "Quantity limit exceeded",
        target: "cart"
      });
    }
    
    return { errors };
  }
};

Layer 5: CAPTCHA Implementation

For high-risk actions, we added Google reCAPTCHA v3:

<!-- In theme.liquid -->
<script src="https://www.google.com/recaptcha/api.js?render={{ settings.recaptcha_site_key }}"></script>

<script>
  function executeRecaptcha(action) {
    return new Promise((resolve, reject) => {
      grecaptcha.ready(function() {
        grecaptcha.execute('{{ settings.recaptcha_site_key }}', {action: action})
          .then(function(token) {
            resolve(token);
          })
          .catch(reject);
      });
    });
  }
  
  // Protect add to cart
  document.addEventListener('submit', async function(e) {
    if (e.target.matches('form[action*="/cart/add"]')) {
      e.preventDefault();
      
      try {
        const token = await executeRecaptcha('add_to_cart');
        
        // Add token to form
        const tokenInput = document.createElement('input');
        tokenInput.type = 'hidden';
        tokenInput.name = 'recaptcha_token';
        tokenInput.value = token;
        e.target.appendChild(tokenInput);
        
        // Submit form
        e.target.submit();
      } catch (error) {
        console.error('reCAPTCHA failed:', error);
      }
    }
  });
</script>

Results

After implementing our multi-layered approach:

MetricBeforeAfterImprovement
Fake Add to Carts~2,000/day~50/day97.5% reduction
Server LoadHigh spikesStable60% reduction in peak load
Analytics AccuracyPoorExcellentClean data
Abandoned Carts85%35%Back to normal

Additional Recommendations

1. Use Shopify’s Built-in Protection

Enable Shopify’s bot protection in your admin:

  • Online Store > Preferences > Enable Google’s reCAPTCHA

2. Monitor and Adapt

Bots evolve, so should your defenses:

// Log suspicious activity for analysis
function logSuspiciousActivity(data) {
  fetch('/apps/security-logger/log', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({
      timestamp: new Date().toISOString(),
      ...data
    })
  });
}

3. Consider Third-Party Solutions

For serious attacks, consider:

  • Cloudflare Bot Management
  • Shopify apps like NoFraud or Signifyd
  • Custom middleware solutions

4. Regular Audits

Weekly checks we now perform:

  • Review cart abandonment rates
  • Check for unusual traffic patterns
  • Monitor server performance metrics
  • Analyze customer complaints

Lessons Learned

  1. Backend events don’t lie: Always compare frontend and backend metrics
  2. Layers are key: No single solution catches all bots
  3. Monitor constantly: Bot patterns change rapidly
  4. Performance matters: Solutions shouldn’t slow down legitimate users
  5. Document everything: Keep logs for pattern analysis

Conclusion

Bot attacks on Shopify stores are increasingly common and sophisticated. While Shopify provides basic protection, serious attacks require a multi-layered approach combining client-side detection, server-side validation, and continuous monitoring.

The key is to make bot operations expensive (in terms of effort) while keeping the experience smooth for real customers. Our solution reduced bot traffic by 97.5% while maintaining a seamless shopping experience for legitimate users.

Remember: bots evolve, and so should your defenses. Stay vigilant, monitor your metrics, and be ready to adapt your strategy as new attack patterns emerge.

Share this article

View More Articles

Stay Ahead of the Curve

Get weekly insights on ecommerce trends, Shopify tips, and growth strategies delivered straight to your inbox.

Join 5,000+ ecommerce professionals. Unsubscribe anytime.