Let's Encrypt Rate Limits: Errors, Workarounds & Reset Times
Fix Let’s Encrypt rate limit errors. Current limits explained, how to check remaining quota, staging environment testing, and strategies to avoid hitting limits. This guide helps you recover from rate-limit hits and adjust your workflows so renewals stay reliable.
Rate Limiting Troubleshooting & Errors
Section titled “Rate Limiting Troubleshooting & Errors”TL;DR: Rate limit errors require waiting for token refill—exact set of identifiers limit (5 per 7 days) can be worked around by modifying the domain set, while authorization failures (5 per hour) reset automatically or after successful validation.
Need help with ACME? Ask Axel Axelspire AI bot with own augmented memory for all ACME/certbot.
Overview
Section titled “Overview”Rate limiting errors disrupt certificate automation and require systematic troubleshooting to identify root causes and implement recovery strategies. This comprehensive guide addresses common rate limiting failures, diagnostic procedures, and proven workarounds based on production deployment experience. Operations teams use these patterns to recover from rate limit scenarios and prevent future occurrences.
Production environments encounter rate limiting when certificate automation workflows exceed Let’s Encrypt’s capacity constraints. Common scenarios include troubleshooting loops that consume the exact set of identifiers limit, authorization failure cascades that trigger hourly limits, and bulk operations that exhaust new orders quota. Understanding error patterns helps teams diagnose issues efficiently and choose appropriate recovery strategies.
Enterprise troubleshooting requires analyzing rate limit error messages, calculating token refill timelines, and implementing workarounds that maintain service availability. Unlike many errors that have immediate fixes, rate limits require patience as capacity refills gradually. Teams must balance operational needs with rate constraints, sometimes using alternative validation methods or modifying certificate scopes.
Common Rate Limiting Errors
Section titled “Common Rate Limiting Errors”Exact Set of Identifiers Limit
Section titled “Exact Set of Identifiers Limit”Error Pattern:
Error creating new order :: too many certificates already issued for exact set of domains: example.com,www.example.com: see https://letsencrypt.org/docs/rate-limits/Details:
- Limit: 5 certificates per exact set of identifiers per 7 days
- Refill Rate: 1 certificate per 34 hours
- Override Available: No
Root Causes:
- Repeated certificate requests during troubleshooting
- Reinstalling ACME client or deleting configuration data
- Testing configuration changes with production certificates
- Automated systems requesting duplicate certificates
Recovery Strategies:
Option 1: Modify Identifier Set (Immediate)
# Original request that hit limit# certbot certonly -d example.com -d www.example.com
# Workaround: Add/remove an identifier to create a different setcertbot certonly -d example.com -d www.example.com -d blog.example.com
# Or remove an identifiercertbot certonly -d example.com
# Note: New set still subject to other limitsOption 2: Wait for Refill
# Calculate when next certificate slot will be available# Limit: 5 certificates per 7 days# Refill: 1 per 34 hours
# Check recent certificates via Certificate Transparencycurl -s "https://crt.sh/?q=example.com&output=json" | \ jq '[.[] | select(.not_before > (now - 604800 | todate))] | {not_before: .not_before, domains: .name_value}' | lessPrevention:
#!/bin/bash# Always test with staging before productioncertbot certonly --staging --dry-run \ --webroot -w /var/www/html \ -d example.com -d www.example.com
# Only request production cert after staging successif [ $? -eq 0 ]; then certbot certonly --staging \ --webroot -w /var/www/html \ -d example.com -d www.example.com
# Final production request certbot certonly \ --webroot -w /var/www/html \ -d example.com -d www.example.comfiNew Orders per Account Limit
Section titled “New Orders per Account Limit”Error Pattern:
Error creating new order :: too many new orders recently (300) for this account, retry after 2026-01-25 18:30:00 UTCDetails:
- Limit: 300 orders per account per 3 hours
- Refill Rate: 1 order per 36 seconds
- Override Available: Yes (for large hosting providers)
Root Causes:
- Bulk certificate operations without rate limiting
- Automated renewal scripts running too frequently
- Multiple services sharing the same ACME account
- Testing or development using production accounts
Recovery Strategies:
Option 1: Wait for Token Refill
# Parse Retry-After from error message# "retry after 2026-01-25 18:30:00 UTC"
# Calculate wait timeRETRY_AFTER="2026-01-25 18:30:00"RETRY_EPOCH=$(date -d "$RETRY_AFTER" +%s)NOW_EPOCH=$(date +%s)WAIT_SECONDS=$(( RETRY_EPOCH - NOW_EPOCH ))
echo "Must wait $WAIT_SECONDS seconds ($((WAIT_SECONDS / 60)) minutes)"
# Automated wait and retrysleep $WAIT_SECONDScertbot certonly --webroot -w /var/www/html -d example.comOption 2: Use Multiple ACME Accounts
# Create separate accounts for different servicescertbot register --email service1@example.com --account service1certbot register --email service2@example.com --account service2
# Use specific account for requestscertbot certonly --account service1 \ --webroot -w /var/www/html -d app1.example.com
certbot certonly --account service2 \ --webroot -w /var/www/html -d app2.example.comOption 3: Request Override (Large Hosting Providers)
# Submit override request (processing takes weeks)# https://isrg.formstack.com/forms/rate_limit_adjustment_request
# Provide:# - Account ID# - Expected certificate volume# - Business justification# - Infrastructure detailsPrevention:
#!/bin/bash# Enterprise-grade order rate limiting
ACCOUNT="production"MAX_ORDERS_PER_HOUR=90 # Conservative limit (300/3h = 100/h)ORDER_COUNT_FILE="/var/cache/certbot-orders.count"
# Track orders in current hourCURRENT_HOUR=$(date +%Y%m%d%H)if [ -f "$ORDER_COUNT_FILE" ]; then STORED_HOUR=$(head -n1 "$ORDER_COUNT_FILE") STORED_COUNT=$(tail -n1 "$ORDER_COUNT_FILE")
if [ "$CURRENT_HOUR" = "$STORED_HOUR" ]; then if [ "$STORED_COUNT" -ge "$MAX_ORDERS_PER_HOUR" ]; then echo "Order limit reached for this hour, waiting..." sleep 3600 fi ORDERS=$((STORED_COUNT + 1)) else ORDERS=1 fielse ORDERS=1fi
# Update counterecho "$CURRENT_HOUR" > "$ORDER_COUNT_FILE"echo "$ORDERS" >> "$ORDER_COUNT_FILE"
# Proceed with ordercertbot certonly --account "$ACCOUNT" \ --webroot -w /var/www/html -d example.comNew Certificates per Registered Domain Limit
Section titled “New Certificates per Registered Domain Limit”Error Pattern:
Error creating new order :: too many certificates already issued for registered domain "example.com": 50, retry after 2026-01-26 12:00:00 UTCDetails:
- Limit: 50 certificates per registered domain per 7 days
- Refill Rate: 1 certificate per 202 minutes (approximately 3.4 hours)
- Override Available: Yes (for large hosting providers)
Root Causes:
- Large-scale certificate deployment or migration
- Multiple subdomains requiring individual certificates
- Development/testing consuming production quota
- Automated systems creating redundant certificates
Recovery Strategies:
Option 1: Consolidate Identifiers
# Instead of separate certificates:# certbot certonly -d app1.example.com# certbot certonly -d app2.example.com# certbot certonly -d app3.example.com
# Use single certificate with multiple identifiers (up to 100)certbot certonly \ -d app1.example.com \ -d app2.example.com \ -d app3.example.com \ -d app4.example.com
# Benefits:# - Consumes only 1 certificate from domain limit# - Still subject to exact set limit (5 per 7 days)Option 2: Use Subdomains Under Different Registered Domains
# If hitting limit on example.com, use subdomain under different registered domain# Registered domains are determined by Public Suffix List
# Instead of: app.example.com (counts against example.com)# Use: app.example.co.uk (counts against example.co.uk if separate registered domain)
# Check registered domain at: https://publicsuffix.org/list/Option 3: Wait for Refill
#!/bin/bash# Monitor domain certificate count
DOMAIN="example.com"LIMIT=50
check_domain_certificates() { # Query Certificate Transparency logs COUNT=$(curl -s "https://crt.sh/?q=${DOMAIN}&output=json" | \ jq '[.[] | select(.not_before > (now - 604800 | todate))] | length' 2>/dev/null)
echo "Certificates for $DOMAIN in last 7 days: $COUNT/$LIMIT"
if [ "$COUNT" -ge "$LIMIT" ]; then echo "Rate limit reached. Capacity refills at 1 per 202 minutes." echo "Next certificate available in approximately: $((202 - (COUNT - LIMIT) * 202)) minutes" return 1 fi return 0}
check_domain_certificatesAuthorization Failures Limit
Section titled “Authorization Failures Limit”Error Pattern:
Error finalizing order :: too many failed authorizations recently: 5 for identifier "example.com"Details:
- Limit: 5 authorization failures per identifier per account per hour
- Refill Rate: 1 failure per 12 minutes
- Override Available: No
Root Causes:
- Misconfigured DNS records
- Firewall blocking validation requests
- Web server configuration issues
- Incorrect challenge type selection
Recovery Strategies:
Option 1: Fix Underlying Issue Then Wait
#!/bin/bash# Troubleshoot and fix before retrying
DOMAIN="example.com"
# Test HTTP-01 challenge accessibilityecho "Testing HTTP-01 challenge endpoint..."curl -I "http://${DOMAIN}/.well-known/acme-challenge/test-file"
# Test DNS resolutionecho "Testing DNS resolution..."dig +short "$DOMAIN" Adig +short "$DOMAIN" AAAA
# Test DNS-01 if usingecho "Testing DNS-01 TXT record..."dig +short "_acme-challenge.${DOMAIN}" TXT
# Only retry after fixes and waiting 1 hourecho "Authorization failures reset after 1 hour of no failures"echo "Next attempt available at: $(date -d '+1 hour' '+%Y-%m-%d %H:%M:%S')"Option 2: Use Different Challenge Type
# If HTTP-01 failing, try DNS-01certbot certonly --dns-cloudflare \ --dns-cloudflare-credentials ~/.secrets/cloudflare.ini \ -d example.com
# If DNS-01 failing, try HTTP-01certbot certonly --webroot -w /var/www/html \ -d example.comPrevention:
#!/bin/bash# Pre-flight checks before certificate request
DOMAIN="example.com"WEBROOT="/var/www/html"
echo "Running pre-flight checks for $DOMAIN..."
# Check DNS resolutionif ! dig +short "$DOMAIN" A | grep -q .; then echo "ERROR: DNS A record not found for $DOMAIN" exit 1fi
# Check HTTP accessibilityif ! curl -f -s -I "http://${DOMAIN}" > /dev/null; then echo "ERROR: HTTP not accessible for $DOMAIN" exit 1fi
# Check webroot accessibilityif [ ! -d "$WEBROOT/.well-known/acme-challenge" ]; then echo "Creating challenge directory..." mkdir -p "$WEBROOT/.well-known/acme-challenge"fi
# Test challenge file servingTEST_TOKEN="test-$(date +%s)"echo "test" > "$WEBROOT/.well-known/acme-challenge/$TEST_TOKEN"if ! curl -f -s "http://${DOMAIN}/.well-known/acme-challenge/$TEST_TOKEN" | grep -q "test"; then echo "ERROR: Cannot serve challenge files for $DOMAIN" rm "$WEBROOT/.well-known/acme-challenge/$TEST_TOKEN" exit 1firm "$WEBROOT/.well-known/acme-challenge/$TEST_TOKEN"
echo "Pre-flight checks passed. Proceeding with certificate request..."certbot certonly --webroot -w "$WEBROOT" -d "$DOMAIN"Consecutive Authorization Failures
Section titled “Consecutive Authorization Failures”Error Pattern:
Your account has an issue. Please contact support.Details:
- Limit: 1,152 consecutive authorization failures
- Recovery: Resets after 1 successful authorization
- Override Available: No
Root Causes:
- Persistent configuration problems causing repeated failures
- Automated renewal systems with systemic issues
- Long-running validation problems across many domains
Recovery Strategy:
#!/bin/bash# Recover from consecutive failure limit
echo "Account paused due to consecutive authorization failures"echo "Must complete 1 successful authorization to reset counter"
# Strategy: Find ONE domain that can validate successfully# Use most reliable configuration
RELIABLE_DOMAIN="test.example.com"WEBROOT="/var/www/html"
# Ensure perfect configurationmkdir -p "$WEBROOT/.well-known/acme-challenge"chmod 755 "$WEBROOT/.well-known/acme-challenge"
# Test manually firstTEST_TOKEN="recovery-test-$(date +%s)"echo "test-content" > "$WEBROOT/.well-known/acme-challenge/$TEST_TOKEN"
echo "Verify this URL returns test-content:"echo "http://${RELIABLE_DOMAIN}/.well-known/acme-challenge/$TEST_TOKEN"read -p "Press enter when verified..."
# Request certificate for reliable domaincertbot certonly --webroot -w "$WEBROOT" -d "$RELIABLE_DOMAIN"
if [ $? -eq 0 ]; then echo "SUCCESS! Consecutive failure counter has been reset." echo "You can now proceed with other certificate requests."else echo "FAILED! Fix validation issues before retrying."fi
rm "$WEBROOT/.well-known/acme-challenge/$TEST_TOKEN"Overall Request Limits (Per-Endpoint)
Section titled “Overall Request Limits (Per-Endpoint)”Error Pattern:
503 Service UnavailableRetry-After: 60Details:
- Load balancer enforces per-IP per-second limits by endpoint
- Example: /acme/new-order limited to 300 req/sec with burst of 200
- Response includes Retry-After header
Recovery Strategy:
#!/usr/bin/env python3# Handle 503 rate limiting with exponential backoff
import requestsimport timefrom datetime import datetime
class ACMEClientWithBackoff: def __init__(self, base_url): self.base_url = base_url self.max_retries = 5
def make_request(self, endpoint, method='GET', **kwargs): retry_count = 0 backoff = 1
while retry_count < self.max_retries: response = requests.request(method, f"{self.base_url}/{endpoint}", **kwargs)
if response.status_code == 503: # Parse Retry-After header retry_after = int(response.headers.get('Retry-After', backoff)) print(f"503 Service Unavailable. Waiting {retry_after} seconds...") time.sleep(retry_after)
retry_count += 1 backoff = min(backoff * 2, 300) # Max 5 minutes continue
return response
raise Exception(f"Max retries ({self.max_retries}) exceeded")
# Usageclient = ACMEClientWithBackoff("https://acme-v02.api.letsencrypt.org")response = client.make_request("directory")Enterprise Troubleshooting Patterns
Section titled “Enterprise Troubleshooting Patterns”Rate Limit Detection and Alerting
Section titled “Rate Limit Detection and Alerting”#!/bin/bash# Automated rate limit detection
LOGFILE="/var/log/letsencrypt/letsencrypt.log"ALERT_EMAIL="ops@example.com"
# Check for rate limit errorsRATE_LIMIT_ERRORS=$(grep -c "too many" "$LOGFILE" 2>/dev/null)
if [ "$RATE_LIMIT_ERRORS" -gt 0 ]; then # Parse error details LATEST_ERROR=$(grep "too many" "$LOGFILE" | tail -1)
# Extract retry time RETRY_TIME=$(echo "$LATEST_ERROR" | grep -oP 'retry after \K[^,]*')
# Send alert mail -s "ALERT: Let's Encrypt Rate Limit Hit" "$ALERT_EMAIL" <<EOFRate limit error detected at $(date)
Latest error:$LATEST_ERROR
Retry after: $RETRY_TIME
Action Required:1. Review recent certificate requests2. Check for automation loops3. Wait for rate limit reset4. Consider using staging environment for testing
Logs: $LOGFILEEOFfiMulti-Account Load Distribution
Section titled “Multi-Account Load Distribution”#!/bin/bash# Distribute certificate requests across multiple accounts
ACCOUNTS=("account1" "account2" "account3")DOMAINS=("app1.example.com" "app2.example.com" "app3.example.com" "app4.example.com")
# Round-robin distributionfor i in "${!DOMAINS[@]}"; do DOMAIN="${DOMAINS[$i]}" ACCOUNT="${ACCOUNTS[$((i % ${#ACCOUNTS[@]}))]}"
echo "Requesting certificate for $DOMAIN using $ACCOUNT" certbot certonly --account "$ACCOUNT" \ --webroot -w /var/www/html \ -d "$DOMAIN"
# Rate limit between requests sleep 2doneRelated Documentation
Section titled “Related Documentation”- Rate Limiting Overview - Core concepts and quick reference
- Rate Limiting API Reference - Endpoint limits and token bucket details
- Rate Limiting Commands - Command-line usage and monitoring
- Certificate Lifecycle Management - Automated renewal strategies
- HTTP-01 Challenge Troubleshooting - Authorization failure diagnosis
- DNS-01 Challenge Validation - Alternative validation method