How to Handle Rate Limiting in FckSignups: Cloudflare Workers and Exponential Backoff Explained
FckSignups handles rate limiting through a two-tier strategy: Cloudflare's native rate-limiting service automatically throttles abusive incoming traffic, while custom exponential-backoff retry logic manages rate-limited responses from external APIs like GitHub.
FckSignups (archived as NoSignups) is a privacy-focused tool directory that relies on a Cloudflare Worker as its API gateway. Understanding how this open-source project handles rate limiting is essential for anyone building serverless architectures that interact with third-party services. This article examines the complete rate-limiting implementation across the Cloudflare Worker layer, external API integrations, and React frontend.
Cloudflare Worker: Native Rate Limiting for Incoming Requests
The first line of defense operates at the edge. In [cloudflare-worker/worker.ts](https://github.com/BraveOPotato/FckSignups/blob/main/cloudflare-worker/worker.ts), the entry point simply routes requests to handlers without implementing custom throttling logic.
Cloudflare's Rate Limiting service handles protection automatically when configured in the dashboard:
- Rule example: 10 requests per 10 seconds per IP address
- Testing mode: Enable "Simulate" to preview blocked requests without enforcement
- Response: HTTP 429 with
Retry-Afterheader when triggered
The worker never sees excessive requests—Cloudflare drops or delays them at the network edge. This zero-code approach keeps the worker lightweight while providing robust DDoS protection.
External API Rate Limiting: GitHub API Integration
The worker implements custom retry logic for outbound calls to rate-limited services. In [cloudflare-worker/urlHandlers/handleSubmitTool.ts](https://github.com/BraveOPotato/FckSignups/blob/main/cloudflare-worker/urlHandlers/handleSubmitTool.ts), submitting a new tool creates a GitHub issue via the REST API—a service with strict rate limits (5000 requests/hour for authenticated users).
Detecting Rate-Limit Responses
The handler checks for two status codes indicating throttling:
const res = await fetch(githubApiUrl, { … });
if (res.status === 429 || res.status === 403) {
const retryAfter = Number(res.headers.get('Retry-After')) || 0;
// Proceed to exponential backoff
}
GitHub returns 403 Forbidden with rate-limit headers when the limit is exceeded, while 429 Too Many Requests is the standard HTTP status. Both are handled identically.
Exponential Backoff Implementation
The retry helper lives in [cloudflare-worker/utils.ts](https://github.com/BraveOPotato/FckSignups/blob/main/cloudflare-worker/utils.ts):
export async function withRetry<T>(fn: () => Promise<T>, maxAttempts = 3): Promise<T> {
let attempt = 0;
while (true) {
try {
return await fn();
} catch (err: any) {
if (attempt++ >= maxAttempts) throw err;
const delay = (err.headers?.get('Retry-After')
? Number(err.headers.get('Retry-After')) * 1000
: Math.pow(2, attempt) * 1000);
await new Promise(r => setTimeout(r, delay));
}
}
}
Algorithm behavior:
- First retry: 2 seconds (2¹ × 1000ms)
- Second retry: 4 seconds (2² × 1000ms)
- Third retry: 8 seconds (2³ × 1000ms)
- Respect
Retry-After: If the API provides this header, use its value instead of calculated backoff
Applied in Handler Code
Here's how handleSubmitTool.ts uses the wrapper:
import { withRetry } from '../utils';
export async function handleSubmitTool(request: Request, env: Env, origin: string) {
// Validation and payload construction omitted
const res = await withRetry(() =>
fetch(`https://api.github.com/repos/${env.GITHUB_REPO_OWNER}/${env.GITHUB_REPO_NAME}/issues`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${env.GITHUB_TOKEN}`,
'Accept': 'application/vnd.github+json',
'Content-Type': 'application/json',
},
body: JSON.stringify(issueBody),
})
);
return new Response(JSON.stringify({ success: true, issueUrl: res.url }), {
headers: { 'Content-Type': 'application/json' },
});
}
The same pattern appears in:
- [
handleReportTool.ts](https://github.com/BraveOPotato/FckSignups/blob/main/cloudflare-worker/urlHandlers/handleReportTool.ts) — reporting problematic tools - [
handleSuggestTool.ts](https://github.com/BraveOPotato/FckSignups/blob/main/cloudflare-worker/urlHandlers/handleSuggestTool.ts) — suggesting edits to existing entries
Frontend: Propagating Rate-Limit Errors
The React frontend in [src/hooks/useTools.ts](https://github.com/BraveOPotato/FckSignups/blob/main/src/hooks/useTools.ts) doesn't implement client-side retry logic. Instead, it surfaces 429 responses from the worker directly to the UI:
import { useQuery } from '@tanstack/react-query';
export default function useTools() {
return useQuery({
queryKey: ['tools'],
queryFn: async () => {
const res = await fetch('/api/tools');
if (!res.ok) {
const error = new Error(res.statusText);
(error as any).status = res.status;
throw error;
}
return res.json();
},
staleTime: 5 * 60 * 1000, // 5 minutes
});
}
Component-level handling:
import useTools from '@/hooks/useTools';
function ToolsList() {
const { data, error, isLoading } = useTools();
if (error?.status === 429) {
return <p>Rate limit reached – please wait a moment and try again.</p>;
}
if (isLoading) return <Spinner />;
return (
<div>
{data.map(tool => <ToolCard key={tool.id} {...tool} />)}
</div>
);
}
This architecture keeps the frontend simple and stateless. All retry complexity lives in the worker, where it can be centrally maintained and audited.
Configuration and Deployment
Setting up Cloudflare's native rate limiting:
- Navigate to Security > WAF > Rate limiting rules in the Cloudflare dashboard
- Create a rule matching your worker's route pattern
- Set thresholds based on expected legitimate traffic
- Enable logging to analyze patterns before enforcement
Environment variables required for the worker:
// wrangler.toml or Cloudflare dashboard
[env.production.vars]
GITHUB_REPO_OWNER = "your-username"
GITHUB_REPO_NAME = "your-repo"
GITHUB_TOKEN = "ghp_xxxx" // Fine-grained personal access token
Summary
- Edge protection: Cloudflare's native rate limiting handles incoming request throttling without code changes in
worker.ts - API resilience: The
withRetryhelper inutils.tsimplements exponential backoff withRetry-Afterheader support for GitHub API calls - Handler coverage: Submit, report, and suggest tool handlers all use consistent retry patterns
- Frontend simplicity: React hooks propagate 429 errors to the UI without client-side retry complexity
- Deploy-time configuration: Rate limits are tuned via Cloudflare dashboard, not hardcoded
Frequently Asked Questions
How does FckSignups handle GitHub API rate limits specifically?
The worker detects 403 and 429 status codes from GitHub API responses, extracts the Retry-After header if present, and retries the request after that delay. If no header is provided, it falls back to exponential backoff with base-2 progression (2s, 4s, 8s). After 3 failed attempts, the error propagates to the client.
Why doesn't the frontend implement its own retry logic?
All retry logic is centralized in the Cloudflare Worker to avoid thundering herd problems and ensure consistent behavior. The frontend simply displays rate-limit errors to users. This separation of concerns keeps the React bundle smaller and prevents conflicting retry strategies between client and server.
Can I adjust the rate limits without changing code?
Yes. Cloudflare Rate Limiting rules are configured entirely through the dashboard. You can modify thresholds, time windows, and enforcement actions without redeploying the worker. The worker's retry logic for external APIs uses environment variables for configuration, so you can adjust maxAttempts by modifying the withRetry call parameters in each handler.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →