Skip to content

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.

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.

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.

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:

  1. Repeated certificate requests during troubleshooting
  2. Reinstalling ACME client or deleting configuration data
  3. Testing configuration changes with production certificates
  4. Automated systems requesting duplicate certificates

Recovery Strategies:

Option 1: Modify Identifier Set (Immediate)

Terminal window
# Original request that hit limit
# certbot certonly -d example.com -d www.example.com
# Workaround: Add/remove an identifier to create a different set
certbot certonly -d example.com -d www.example.com -d blog.example.com
# Or remove an identifier
certbot certonly -d example.com
# Note: New set still subject to other limits

Option 2: Wait for Refill

Terminal window
# Calculate when next certificate slot will be available
# Limit: 5 certificates per 7 days
# Refill: 1 per 34 hours
# Check recent certificates via Certificate Transparency
curl -s "https://crt.sh/?q=example.com&output=json" | \
jq '[.[] | select(.not_before > (now - 604800 | todate))] |
{not_before: .not_before, domains: .name_value}' | less

Prevention:

#!/bin/bash
# Always test with staging before production
certbot certonly --staging --dry-run \
--webroot -w /var/www/html \
-d example.com -d www.example.com
# Only request production cert after staging success
if [ $? -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.com
fi

Error Pattern:

Error creating new order :: too many new orders recently (300) for this account, retry after 2026-01-25 18:30:00 UTC

Details:

  • Limit: 300 orders per account per 3 hours
  • Refill Rate: 1 order per 36 seconds
  • Override Available: Yes (for large hosting providers)

Root Causes:

  1. Bulk certificate operations without rate limiting
  2. Automated renewal scripts running too frequently
  3. Multiple services sharing the same ACME account
  4. Testing or development using production accounts

Recovery Strategies:

Option 1: Wait for Token Refill

Terminal window
# Parse Retry-After from error message
# "retry after 2026-01-25 18:30:00 UTC"
# Calculate wait time
RETRY_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 retry
sleep $WAIT_SECONDS
certbot certonly --webroot -w /var/www/html -d example.com

Option 2: Use Multiple ACME Accounts

Terminal window
# Create separate accounts for different services
certbot register --email service1@example.com --account service1
certbot register --email service2@example.com --account service2
# Use specific account for requests
certbot certonly --account service1 \
--webroot -w /var/www/html -d app1.example.com
certbot certonly --account service2 \
--webroot -w /var/www/html -d app2.example.com

Option 3: Request Override (Large Hosting Providers)

Terminal window
# Submit override request (processing takes weeks)
# https://isrg.formstack.com/forms/rate_limit_adjustment_request
# Provide:
# - Account ID
# - Expected certificate volume
# - Business justification
# - Infrastructure details

Prevention:

#!/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 hour
CURRENT_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
fi
else
ORDERS=1
fi
# Update counter
echo "$CURRENT_HOUR" > "$ORDER_COUNT_FILE"
echo "$ORDERS" >> "$ORDER_COUNT_FILE"
# Proceed with order
certbot certonly --account "$ACCOUNT" \
--webroot -w /var/www/html -d example.com

New 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 UTC

Details:

  • 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:

  1. Large-scale certificate deployment or migration
  2. Multiple subdomains requiring individual certificates
  3. Development/testing consuming production quota
  4. Automated systems creating redundant certificates

Recovery Strategies:

Option 1: Consolidate Identifiers

Terminal window
# 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

Terminal window
# 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_certificates

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:

  1. Misconfigured DNS records
  2. Firewall blocking validation requests
  3. Web server configuration issues
  4. 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 accessibility
echo "Testing HTTP-01 challenge endpoint..."
curl -I "http://${DOMAIN}/.well-known/acme-challenge/test-file"
# Test DNS resolution
echo "Testing DNS resolution..."
dig +short "$DOMAIN" A
dig +short "$DOMAIN" AAAA
# Test DNS-01 if using
echo "Testing DNS-01 TXT record..."
dig +short "_acme-challenge.${DOMAIN}" TXT
# Only retry after fixes and waiting 1 hour
echo "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

Terminal window
# If HTTP-01 failing, try DNS-01
certbot certonly --dns-cloudflare \
--dns-cloudflare-credentials ~/.secrets/cloudflare.ini \
-d example.com
# If DNS-01 failing, try HTTP-01
certbot certonly --webroot -w /var/www/html \
-d example.com

Prevention:

#!/bin/bash
# Pre-flight checks before certificate request
DOMAIN="example.com"
WEBROOT="/var/www/html"
echo "Running pre-flight checks for $DOMAIN..."
# Check DNS resolution
if ! dig +short "$DOMAIN" A | grep -q .; then
echo "ERROR: DNS A record not found for $DOMAIN"
exit 1
fi
# Check HTTP accessibility
if ! curl -f -s -I "http://${DOMAIN}" > /dev/null; then
echo "ERROR: HTTP not accessible for $DOMAIN"
exit 1
fi
# Check webroot accessibility
if [ ! -d "$WEBROOT/.well-known/acme-challenge" ]; then
echo "Creating challenge directory..."
mkdir -p "$WEBROOT/.well-known/acme-challenge"
fi
# Test challenge file serving
TEST_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 1
fi
rm "$WEBROOT/.well-known/acme-challenge/$TEST_TOKEN"
echo "Pre-flight checks passed. Proceeding with certificate request..."
certbot certonly --webroot -w "$WEBROOT" -d "$DOMAIN"

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:

  1. Persistent configuration problems causing repeated failures
  2. Automated renewal systems with systemic issues
  3. 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 configuration
mkdir -p "$WEBROOT/.well-known/acme-challenge"
chmod 755 "$WEBROOT/.well-known/acme-challenge"
# Test manually first
TEST_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 domain
certbot 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"

Error Pattern:

503 Service Unavailable
Retry-After: 60

Details:

  • 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 requests
import time
from 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")
# Usage
client = ACMEClientWithBackoff("https://acme-v02.api.letsencrypt.org")
response = client.make_request("directory")
#!/bin/bash
# Automated rate limit detection
LOGFILE="/var/log/letsencrypt/letsencrypt.log"
ALERT_EMAIL="ops@example.com"
# Check for rate limit errors
RATE_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" <<EOF
Rate limit error detected at $(date)
Latest error:
$LATEST_ERROR
Retry after: $RETRY_TIME
Action Required:
1. Review recent certificate requests
2. Check for automation loops
3. Wait for rate limit reset
4. Consider using staging environment for testing
Logs: $LOGFILE
EOF
fi
#!/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 distribution
for 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 2
done