A strong bot protection system for Flask with many features: rate limiting, special rules for users, web crawler detection, and automatic bot detection.
from flask import Flask
from flask_humanify import Humanify
app = Flask(__name__)
humanify = Humanify(app, challenge_type="one_click", image_dataset="ai_dogs")
# Register the middleware to deny access to bots
humanify.register_middleware(app, action="challenge")
@app.route("/")
def index():
"""
A route that is protected against bots and DDoS attacks.
"""
return "Hello, Human!"
if __name__ == "__main__":
app.run()You can customize bot protection with advanced filtering rules:
# Protect specific endpoints with regex patterns
humanify.register_middleware(
app,
action="challenge",
endpoint_patterns=["api.*", "admin.*"] # Protect all API and admin endpoints
)
# Protect specific URL paths
humanify.register_middleware(
app,
action="deny_access",
url_patterns=["/sensitive/*", "/admin/*"] # Deny bot access to sensitive areas
)
# Exclude certain patterns from protection
humanify.register_middleware(
app,
endpoint_patterns=["api.*"],
exclude_patterns=["api.public.*"] # Don't protect public API endpoints
)
# Filter by request parameters
humanify.register_middleware(
app,
request_filters={
"method": ["POST", "PUT", "DELETE"], # Only protect write operations
"args.admin": "true", # Only when admin=true query parameter exists
"headers.content-type": "regex:application/json.*" # Match content type with regex
}
)Flask-Humanify provides powerful decorators for fine-grained bot protection on specific routes:
Protects a route by challenging suspected bots with a captcha.
from flask_humanify import require_human
@app.route("/protected")
@require_human()
def protected():
return "Only humans can access this"
@app.route("/strict")
@require_human(action="deny_access")
def strict():
return "Bots are blocked without a challenge"Parameters:
action(str): Action to take when a bot is detected"challenge"(default): Show captcha challenge"deny_access": Block access immediately
Forces all visitors (including humans) to solve a captcha before accessing the route.
from flask_humanify import always_challenge
@app.route("/sensitive")
@always_challenge
def sensitive():
return "Everyone must solve a captcha"Immediately blocks all detected bots without showing a captcha challenge.
from flask_humanify import block_bots
@app.route("/no-bots")
@block_bots
def no_bots():
return "Bots cannot access this route"Exempts a route from all Humanify protection, including middleware rules.
from flask_humanify import exempt_from_protection
@app.route("/public-api")
@exempt_from_protection
def public_api():
return {"data": "No bot protection on this endpoint"}When both middleware and decorators are used:
@exempt_from_protection- Highest priority, bypasses all protection- Route-specific decorators - Override middleware settings for that route
- Middleware rules - Apply to routes without decorators
Install the package with pip:
pip install flask-humanify --upgradeFor better performance and protection against ReDoS (Regular Expression Denial of Service) attacks, install the google-re2 library:
pip install google-re2Flask-Humanify will automatically use re2 if available, providing:
- Guaranteed linear-time regex execution (no catastrophic backtracking)
- Better performance with complex patterns
- Enhanced security when using custom
endpoint_patterns,url_patterns, orrequest_filters
The library works without re2, but installing it is recommended for production environments.
from flask import Flask
from flask_humanify import Humanify
app = Flask(__name__)
humanify = Humanify(app)Humanify can be configured with various options to customize bot detection and challenge behavior:
humanify = Humanify(
app,
challenge_type="one_click", # Challenge type: "grid" or "one_click"
image_dataset="ai_dogs", # Image dataset: "ai_dogs", "animals", "characters", "keys"
audio_dataset=None, # Enable audio challenges: "characters" or None
retrys=3, # Maximum failed attempts before blocking
hardness=1, # Challenge difficulty: 1 (easy) to 5 (hard)
behind_proxy=False, # Set True if behind a proxy/load balancer
use_client_id=False # Use secure client IDs instead of IP addresses
)Configuration Parameters:
-
challenge_type: Type of visual challenge"one_click": Select one matching image from a grid (easier, faster)"grid": Select multiple matching images from a 3x3 grid (harder)
-
image_dataset: Dataset for image challenges"ai_dogs": AI-generated dog images"animals": Various animal images"characters": Character/letter recognition"keys": Key/lock matching
-
audio_dataset: Enable audio accessibility challengesNone: Disabled (default)"characters": Audio character recognition in multiple languages
-
retrys: Number of failed attempts before temporary block (default: 3) -
hardness: Challenge difficulty level (1-5)1: Easy - minimal distortion3: Medium - moderate distortion5: Hard - maximum distortion
-
behind_proxy: Enable when behind reverse proxy/load balancer- Automatically configures ProxyFix for correct IP detection
-
use_client_id: Use secure client IDs instead of IP addresses- Better for privacy and shared IP scenarios
- Stored in secure HTTP-only cookies
Flask-Humanify includes a powerful rate limiting feature to protect your application from excessive requests:
from flask import Flask
from flask_humanify import Humanify, RateLimiter
app = Flask(__name__)
humanify = Humanify(app)
# Initialize with default limits (10 requests per 10 seconds)
rate_limiter = RateLimiter(app)
# Or use human-readable limit strings
rate_limiter = RateLimiter(app, default_limit="100/day")
# Configure client tracking (defaults to IP-based)
rate_limiter = RateLimiter(
app,
default_limit="10/minute",
use_client_id=True, # Use secure client IDs instead of IPs
behind_proxy=True # Enable if behind a proxy/load balancer
)You can set different rate limits for specific routes or patterns:
# Using decorator syntax
@app.route("/api/data")
@rate_limiter.limit("5/minute") # Limit specific route
def get_data():
return "data"
# Using pattern matching
rate_limiter.set_route_limit("/api/*", "100/hour") # All API routes
rate_limiter.set_route_limit("/admin/<id>", "5/minute") # Admin routes
# Exempt routes from rate limiting
@app.route("/health")
@rate_limiter.exempt
def health_check():
return "OK"The rate limiter provides methods for managing and monitoring rate limits:
# Reset rate limits for a client
rate_limiter.reset_client("client_id") # Reset all routes
rate_limiter.reset_client("client_id", "/api/data:GET") # Reset specific route
# Get client statistics
stats = rate_limiter.get_client_stats("client_id")
"""
Returns:
{
"route:method": {
"current_requests": 5,
"next_reset": 1629123456.78
}
}
"""
# Check rate limits programmatically
if rate_limiter.is_rate_limited():
return "Too many requests!"Features:
- Flexible rate limit formats: "10/second", "5 per minute", "100/day"
- Route-specific rate limits using decorators or patterns
- Client tracking via IPs or secure client IDs
- Proxy support with X-Forwarded-For headers
- Route exemptions for health checks and critical endpoints
- Built-in rate limit monitoring and management
- Automatic rate limit page with return URL
Flask-Humanify includes built-in support for multiple CAPTCHA providers to add an extra layer of protection:
from flask import Flask
from flask_humanify import CaptchaEmbed
app = Flask(__name__)
# Initialize CAPTCHA with automatic theme detection and language
captcha = CaptchaEmbed(
app,
theme="auto", # Options: "light", "dark", "auto"
language="auto", # Use specific language code like "en" if needed
recaptcha_site_key="your_site_key", # For Google reCAPTCHA
recaptcha_secret="your_secret_key",
hcaptcha_site_key="your_site_key", # For hCaptcha
hcaptcha_secret="your_secret_key",
turnstile_site_key="your_site_key", # For Cloudflare Turnstile
turnstile_secret="your_secret_key",
friendly_site_key="your_site_key", # For Friendly Captcha
friendly_secret="your_secret_key",
altcha_secret="your_secret_key" # For Altcha (a random generated secret)
)
@app.route("/protected", methods=["GET", "POST"])
def protected():
if request.method == "POST":
# Validate the CAPTCHA response
if captcha.is_recaptcha_valid(): # Or use is_hcaptcha_valid(), is_turnstile_valid(), etc.
return "Success!"
return render_template("form.html")In your templates, you can easily embed any supported CAPTCHA:
<!-- Templates automatically get access to CAPTCHA embeds -->
<form method="POST">
{{ recaptcha|safe }}
<!-- For Google reCAPTCHA -->
{{ hcaptcha|safe }}
<!-- For hCaptcha -->
{{ turnstile|safe }}
<!-- For Cloudflare Turnstile -->
{{ friendly|safe }}
<!-- For Friendly Captcha -->
{{ altcha|safe }}
<!-- For Altcha (with default hardness) -->
{{ altcha1|safe }}
<!-- For Altcha (with hardness level 1-5) -->
<button type="submit">Submit</button>
</form>The CAPTCHA integration features:
- Automatic dark/light theme detection
- Multiple CAPTCHA provider support
- Customizable difficulty levels for Altcha
- Easy validation methods
- Automatic template context integration
Flask-Humanify provides a clean error handling system:
from flask import Flask
from flask_humanify import Humanify, ErrorHandler
app = Flask(__name__)
humanify = Humanify(app)
# Handle all standard HTTP errors
error_handler = ErrorHandler(app)
# Use custom template with placeholders: EXCEPTION_TITLE, EXCEPTION_CODE, EXCEPTION_MESSAGE
error_handler = ErrorHandler(app, template_path="templates/error.html")
# Or handle only specific error codes
error_handler = ErrorHandler(app, errors=[404, 429, 500])
# Or handle only specific error codes with a custom template
error_handler = ErrorHandler(app, errors={404: {"template": "404.html"}})The error handler:
- Renders user-friendly error pages
- Uses the custom exception.html template
- Provides appropriate error messages and descriptions
- Includes HTTP status codes and titles
Flask-Humanify automatically detects various types of suspicious traffic:
- VPN Detection: Identifies traffic from major VPN providers (NordVPN, ProtonVPN, ExpressVPN, etc.)
- Proxy Detection: Detects proxy servers and anonymizers
- Datacenter IPs: Flags requests from datacenter IP ranges
- Tor Exit Nodes: Identifies Tor network exit points
- Web Crawlers: Recognizes legitimate and malicious crawlers via user-agent analysis
- Forum Spammers: Blocks known spam sources from StopForumSpam database
- Firehol Blocklists: Uses Firehol Level 1 blocklist for known malicious IPs
All detection happens automatically with no additional configuration required.
Here's a complete example combining all features:
from flask import Flask
from flask_humanify import (
Humanify,
RateLimiter,
ErrorHandler,
require_human,
always_challenge,
block_bots,
exempt_from_protection,
)
app = Flask(__name__)
# Setup core protection with custom configuration
humanify = Humanify(
app,
challenge_type="one_click",
image_dataset="animals",
audio_dataset="characters", # Enable audio accessibility
retrys=3,
hardness=2,
behind_proxy=True,
use_client_id=True
)
# Protect all API routes with middleware
humanify.register_middleware(
action="challenge",
url_patterns="/api/*",
exclude_patterns="/api/public/*"
)
# Add rate limiting
rate_limiter = RateLimiter(app, default_limit="100/hour")
rate_limiter.set_route_limit("/api/data", "10/minute")
# Add error handling
error_handler = ErrorHandler(app)
@app.route("/")
def index():
return "Hello, Human!"
@app.route("/api/public/status")
@exempt_from_protection
def public_status():
return {"status": "ok"}
@app.route("/login")
@always_challenge
def login():
return "Login page - everyone must solve captcha"
@app.route("/admin")
@block_bots
def admin():
return "Admin area - bots blocked immediately"
@app.route("/protected")
@require_human(action="challenge")
def protected():
return "Protected content"
if __name__ == "__main__":
app.run(debug=True)- Use HTTPS: Always enable HTTPS in production for secure cookie transmission
- Behind Proxy: Set
behind_proxy=Truewhen using reverse proxies or load balancers - Client IDs: Consider
use_client_id=Truefor better privacy and shared IP handling - Rate Limiting: Combine bot protection with rate limiting for comprehensive defense
- Retry Limits: Adjust
retrysbased on your security requirements (lower = stricter) - Challenge Difficulty: Balance
hardnessbetween security and user experience - Pattern Matching: Use specific patterns in middleware to protect only necessary routes
This project is licensed under the MIT License - see the LICENSE file for details.