# How to Handle Rate Limiting in FckSignups: Cloudflare Workers and Exponential Backoff Explained

> Learn how to handle rate limiting in FckSignups using Cloudflare Workers and exponential backoff. Protect your application from abuse and optimize API calls.

- Repository: [Abdullah/FckSignups](https://github.com/BraveOPotato/FckSignups)
- Tags: how-to-guide
- Published: 2026-09-07

---

**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)](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-After` header 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)](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:

```typescript
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)](https://github.com/BraveOPotato/FckSignups/blob/main/cloudflare-worker/utils.ts):

```typescript
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:**

1. **First retry**: 2 seconds (2¹ × 1000ms)
2. **Second retry**: 4 seconds (2² × 1000ms)
3. **Third retry**: 8 seconds (2³ × 1000ms)
4. **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`](https://github.com/BraveOPotato/FckSignups/blob/main/handleSubmitTool.ts) uses the wrapper:

```typescript
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/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/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)](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:

```typescript
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:**

```tsx
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:

1. Navigate to **Security > WAF > Rate limiting rules** in the Cloudflare dashboard
2. Create a rule matching your worker's route pattern
3. Set thresholds based on expected legitimate traffic
4. Enable logging to analyze patterns before enforcement

Environment variables required for the worker:

```typescript
// 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`](https://github.com/BraveOPotato/FckSignups/blob/main/worker.ts)
- **API resilience**: The `withRetry` helper in [`utils.ts`](https://github.com/BraveOPotato/FckSignups/blob/main/utils.ts) implements exponential backoff with `Retry-After` header 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.