Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Braze Bot Detection for Next.js

A comprehensive Next.js application demonstrating how to prevent bot traffic from inflating your Braze Monthly Active Users (MAU) by implementing multi-factor bot detection before initializing the Braze Web SDK.

🎯 Problem Statement

Bot traffic can artificially inflate your Braze MAU count, leading to:

  • Inaccurate analytics and metrics
  • Higher costs (if pricing is based on MAU)
  • Skewed user behavior data
  • Poor data-driven decision making

✨ Solution

This application implements a multi-layered bot detection system that:

  1. Analyzes requests at the edge using Next.js middleware
  2. Uses multiple detection factors: User-Agent, HTTP headers, IP addresses, browser fingerprinting
  3. Prevents SDK initialization for detected bots
  4. Maintains user experience for legitimate users

🏗️ Architecture

┌─────────────┐
│   Request   │
└──────┬──────┘
       │
       ▼
┌──────────────────┐
│  Next.js         │
│  Middleware      │◄─── Multi-factor bot detection
└──────┬───────────┘
       │
       ├─── Sets cookie: { isBot: boolean, confidence: number }
       │
       ▼
┌──────────────────┐
│  Client-Side     │
│  Page Render     │
└──────┬───────────┘
       │
       ▼
┌──────────────────┐
│  Check Cookie    │
└──────┬───────────┘
       │
       ├─── isBot = true  ──→ ❌ Skip Braze initialization
       │
       └─── isBot = false ──→ ✅ Initialize Braze SDK

⚙️ How It Works

When a request hits the Next.js middleware, the detection process follows these steps:

1. Request Fingerprinting

The middleware extracts key information from the incoming request:

// middleware.ts
const fingerprint = createFingerprintFromHeaders(request.headers);
const clientIP = fingerprint.ip || 'unknown';
// lib/bot-detection/detector.ts
export function createFingerprintFromHeaders(headers: Headers): RequestFingerprint {
  return {
    userAgent: headers.get('user-agent') || '',
    ip: extractIP(headers),
    acceptLanguage: headers.get('accept-language') || undefined,
    acceptEncoding: headers.get('accept-encoding') || undefined,
    accept: headers.get('accept') || undefined,
    secFetchSite: headers.get('sec-fetch-site') || undefined,
    secFetchMode: headers.get('sec-fetch-mode') || undefined,
    // ... other headers
  };
}

2. Multi-Factor Analysis

Three independent analyzers evaluate the request:

Analyzer What it checks Confidence weight
User-Agent Bot patterns, browser indicators, isbot library High (0.95 for known bots)
Headers Missing/suspicious headers, browser-likeness score Medium (up to 0.50)
IP Address Cloud provider ranges, datacenter IPs, invalid addresses Low (0.30-0.40)

User-Agent Analysis:

// lib/bot-detection/user-agent-detector.ts
export function analyzeUserAgent(userAgent: string | undefined): UserAgentAnalysis {
  // Check with isbot library (comprehensive bot database)
  if (isbot(userAgent)) {
    return { isBot: true, confidence: 0.95, reasons: ['Detected by isbot library'] };
  }

  // Check custom bot patterns
  const BOT_PATTERNS = [/curl/i, /python-requests/i, /axios/i, /puppeteer/i, ...];
  for (const pattern of BOT_PATTERNS) {
    if (pattern.test(userAgent)) {
      return { isBot: true, confidence: 0.85, reasons: [`Matched: ${pattern}`] };
    }
  }

  // Check for incomplete user-agents
  if (userAgent.length < 20) {
    return { isBot: true, confidence: 0.6, reasons: ['User agent too short'] };
  }

  return { isBot: false, confidence: 0, reasons: [] };
}

Header Analysis:

// lib/bot-detection/header-analyzer.ts
export function analyzeHeaders(fingerprint: RequestFingerprint): HeaderAnalysis {
  let suspicionScore = 0;

  if (!fingerprint.accept) suspicionScore += 20;
  if (!fingerprint.acceptLanguage) suspicionScore += 15;
  if (!fingerprint.acceptEncoding) suspicionScore += 15;
  if (fingerprint.accept === '*/*') suspicionScore += 20;  // Generic = bot-like
  if (!fingerprint.secFetchSite) suspicionScore += 15;     // Missing modern browser headers

  const confidence = Math.min(suspicionScore / 100, 1);
  return { isBot: confidence >= 0.5, confidence, reasons };
}

IP Analysis:

// lib/bot-detection/ip-analyzer.ts
export function analyzeIP(ip: string | undefined): IPAnalysis {
  // Check cloud provider IP ranges (AWS, GCP, Azure, DigitalOcean)
  const CLOUD_PATTERNS = [/^3\./, /^13\./, /^35\./, /^52\./, ...];

  for (const pattern of CLOUD_PATTERNS) {
    if (pattern.test(ip)) {
      return { isBot: false, confidence: 0.4, reasons: ['Cloud provider IP'] };
    }
  }

  if (ip === '0.0.0.0' || ip === '::') {
    return { isBot: true, confidence: 0.8, reasons: ['Invalid IP address'] };
  }

  return { isBot: false, confidence: 0, reasons: [] };
}

3. Confidence Calculation

// lib/bot-detection/detector.ts
// Use max confidence if any method strongly detects, otherwise average
const finalConfidence = maxConfidence > 0.5 ? maxConfidence : averageConfidence;

This ensures a single strong signal (e.g., known bot user-agent) isn't diluted by weaker signals.

4. Bot Classification

// Normal mode: 60% threshold
isBot = finalConfidence >= 0.6;

// Strict mode: lower threshold + multiple reasons
isBot = finalConfidence >= 0.4 || reasons.length >= 3;

5. Response Headers

The middleware sets three headers on every response:

// middleware.ts
response.headers.set('x-bot-detected', analysis.isBot.toString());
response.headers.set('x-bot-confidence', analysis.confidence.toString());
response.headers.set('x-client-ip', clientIP);

These headers (plus a cookie) allow client-side code to check bot status and conditionally initialize SDKs.

🔍 Detection Factors

1. User-Agent Analysis

  • Uses the isbot library (comprehensive bot database)
  • Custom bot patterns (crawlers, scrapers, headless browsers)
  • Validates browser indicators (Chrome, Firefox, Safari)
  • Checks for suspicious patterns and lengths

2. HTTP Headers Analysis

  • Accept header: Bots often use */* instead of browser-specific values
  • Accept-Language: Missing or malformed indicates non-browser
  • Accept-Encoding: Legitimate browsers support gzip/brotli
  • Sec-Fetch-* headers: Modern browser security features
  • Connection header: close more common in bots
  • Upgrade-Insecure-Requests: Present in legitimate browsers

3. IP Address Analysis

  • Identifies cloud provider IPs (AWS, GCP, Azure, DigitalOcean)
  • Detects datacenter vs residential IPs
  • Flags invalid or suspicious IP addresses
  • Handles proxy headers (X-Forwarded-For, X-Real-IP, CF-Connecting-IP)

4. Browser Fingerprinting

  • Language preferences
  • Platform information
  • Vendor strings
  • Cookie support
  • Overall "browser-likeness" scoring

📦 Installation

  1. Clone or create the project:
npm install
  1. Set up environment variables: Create a .env.local file:
NEXT_PUBLIC_BRAZE_API_KEY=your_braze_api_key_here
NEXT_PUBLIC_BRAZE_SDK_ENDPOINT=your_braze_sdk_endpoint_here
NEXT_PUBLIC_BRAZE_APP_VERSION=1.0.0
BOT_DETECTION_STRICT_MODE=false
  1. Run the development server:
npm run dev
  1. Open your browser: Visit http://localhost:3000

🚀 Usage

Basic Usage

The bot detection happens automatically via middleware. In your components:

'use client';

import { useBraze } from '@/lib/braze';

export default function MyComponent() {
  const brazeConfig = {
    apiKey: process.env.NEXT_PUBLIC_BRAZE_API_KEY || '',
    sdkEndpoint: process.env.NEXT_PUBLIC_BRAZE_SDK_ENDPOINT || '',
    enableLogging: true,
  };

  // Automatically checks for bots and conditionally initializes
  const { initialized, isBot, confidence } = useBraze(brazeConfig);

  return (
    <div>
      {isBot ? (
        <p>Bot detected - Braze not initialized</p>
      ) : (
        <p>Braze initialized successfully!</p>
      )}
    </div>
  );
}

Manual Initialization

If you prefer manual control:

import { initializeBraze, getBotDetectionFromCookie } from '@/lib/braze';

// Check bot status
const detection = getBotDetectionFromCookie();

if (!detection?.isBot) {
  // Initialize Braze only for humans
  const result = initializeBraze({
    apiKey: 'your_api_key',
    sdkEndpoint: 'your_endpoint',
  });

  console.log('Braze initialized:', result.initialized);
}

Logging Events

Use the safe wrapper functions that check initialization status:

import { logCustomEvent, changeUser, logPageView } from '@/lib/braze';

// These only execute if Braze is initialized (i.e., not a bot)
logCustomEvent('button_clicked', { button_id: 'signup' });
changeUser('user123');
logPageView('/pricing');

⚙️ Configuration

Strict Mode

Enable more aggressive bot detection:

BOT_DETECTION_STRICT_MODE=true

In strict mode:

  • Lower confidence threshold (40% vs 60%)
  • More sensitive to missing headers
  • Flags ambiguous cases as bots

Custom Detection Logic

You can customize detection in middleware.ts:

const analysis = analyzeRequest(request.headers, {
  strictMode: true,
  checkUserAgent: true,
  checkHeaders: true,
  checkIP: true,
  confidenceThreshold: 0.5, // Custom threshold
});

🧪 Testing

Quick Manual Tests

# Simple curl request (will be detected as bot)
curl http://localhost:3000

# Custom user-agent
curl -H "User-Agent: MyBot/1.0" http://localhost:3000

Bot Simulator (Python)

A comprehensive Python script that simulates bot, human, and suspicious traffic:

# Install dependencies
pip install requests

# Run with default settings (100 requests, 60% bot ratio)
python3 testing/bot-simulator.py

# Custom configuration
python3 testing/bot-simulator.py --requests 50 --threads 5 --bot-ratio 0.7

# Test against a different URL
python3 testing/bot-simulator.py --url https://your-app.vercel.app --requests 30

Options:

  • --url: Target URL (default: http://localhost:3000)
  • --requests: Total number of requests (default: 100)
  • --threads: Number of concurrent threads (default: 10)
  • --bot-ratio: Ratio of bot traffic 0.0-1.0 (default: 0.6)

Output includes:

  • Real-time request logging with IP, detection status, confidence, and user-agent
  • Summary statistics (detection rates, false positives, accuracy)
  • JSON results saved to testing/results/

k6 Load Testing

For performance and load testing with k6:

# Install k6
brew install k6  # macOS
# or visit https://k6.io/docs/getting-started/installation/

# Run the full load test (ramps up to 50 concurrent users)
k6 run testing/k6-load-test.js

# Run a quick test (fewer requests)
k6 run testing/k6-quick-test.js

The k6 tests simulate mixed traffic (60% bots, 30% humans, 10% suspicious) and track:

  • Bot detection rates
  • Human pass-through rates
  • False positives
  • Response times (p95)
  • Detection confidence

Analyzing Results

# Analyze k6 results
node testing/analyze-k6.js

# Analyze bot simulator results (analyzes the latest JSON file)
node testing/analyze-results.js

Results are saved to testing/results/ as JSON files with timestamps.

Debug Page

Visit /debug to see detailed bot detection information:

  • User-Agent analysis
  • Browser fingerprint
  • Cookie values
  • Detection factors and scores

📊 Monitoring

Production Logging

The middleware logs bot detections to console. In production, send to your analytics:

// middleware.ts
if (analysis.isBot) {
  // Send to your analytics service
  await analytics.track('bot_detected', {
    path: request.nextUrl.pathname,
    confidence: analysis.confidence,
    reasons: analysis.reasons,
    userAgent: request.headers.get('user-agent'),
  });
}

Metrics to Track

  • Bot detection rate (% of requests flagged)
  • False positive rate (legitimate users blocked)
  • MAU before/after implementation
  • Confidence score distribution

🔒 Security Considerations

  1. Cookie Security: Bot detection cookie is set with httpOnly: false to allow client access, but uses sameSite: 'strict' and secure in production.

  2. IP Privacy: IP addresses are analyzed but not stored. Consider GDPR/privacy regulations.

  3. False Positives: Monitor and tune thresholds to minimize blocking legitimate users.

  4. Evasion: Sophisticated bots may bypass detection. Use as part of a broader security strategy.

📈 Performance

  • Middleware overhead: ~1-5ms per request
  • Cookie size: ~150 bytes
  • No external API calls: All detection runs locally
  • Caching: Bot detection result cached in cookie for 24 hours

🔧 Troubleshooting

Braze Not Initializing for Legitimate Users

  1. Check browser console for initialization errors
  2. Visit /debug to see detection details
  3. Verify environment variables are set
  4. Lower the confidence threshold in strict mode

False Positives (Users Detected as Bots)

  1. Disable strict mode
  2. Adjust confidenceThreshold in middleware
  3. Check if corporate VPN/proxy is affecting headers
  4. Review detection reasons in logs

Bot Detection Cookie Missing

  1. Check middleware is running (add console.log)
  2. Verify matcher pattern includes your routes
  3. Check for cookie-blocking browser extensions
  4. Ensure middleware.ts is in the root directory

📚 Project Structure

.
├── app/
│   ├── layout.tsx          # Root layout
│   ├── page.tsx            # Home page with bot detection demo
│   ├── debug/
│   │   └── page.tsx        # Debug page showing detection details
│   └── globals.css         # Global styles
├── lib/
│   ├── bot-detection/
│   │   ├── types.ts        # TypeScript types
│   │   ├── user-agent-detector.ts   # UA analysis
│   │   ├── header-analyzer.ts       # HTTP header analysis
│   │   ├── ip-analyzer.ts           # IP address analysis
│   │   ├── detector.ts              # Main detection logic
│   │   └── index.ts                 # Exports
│   └── braze/
│       ├── types.ts                 # Braze types
│       ├── init.ts                  # Braze initialization
│       ├── useBraze.ts              # React hooks
│       └── index.ts                 # Exports
├── testing/
│   ├── bot-simulator.py    # Python bot/human traffic simulator
│   ├── k6-load-test.js     # k6 load testing script
│   ├── k6-quick-test.js    # k6 quick test script
│   ├── analyze-k6.js       # k6 results analyzer
│   ├── analyze-results.js  # Bot simulator results analyzer
│   ├── setup.sh            # Testing environment setup
│   └── results/            # Test results (JSON files)
├── middleware.ts           # Next.js middleware (bot detection)
├── next.config.js          # Next.js configuration
├── tsconfig.json           # TypeScript configuration
├── package.json            # Dependencies
└── .env.example            # Environment variables template

🤝 Contributing

Improvements and suggestions welcome! Key areas:

  • Additional detection heuristics
  • IP intelligence service integration
  • Machine learning models
  • Performance optimizations

📝 License

This is a demonstration project. Use and modify as needed for your use case.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages