A Simple Rate Limiter for CloudFlare Workers (Serverless API) based on KV Stores

in blurt •  2 years ago 

image.png

CloudFlare Workers is a Serverless Technology. We can use it to host the Serverless APIs. However, no matter what the status code or content is returned from the cloudflare worker, it will be counted as one request because the worker function is hit. Therefore, it is necessary to rate limit the CloudFlare Worker APIs in order to avoid surprising billing.

Please note that, the Free Tier of CloudFlare worker has a daily 100K requests cap. Requests exceeding that threshold will fail (either soft or hard returning 1027 status code). For Paid plan, there is no Usage Cap, but we can always set a Usage-based Notification once the number of requests exceeds a threshold.

Using CloudFlare Rate Limiter Product to Rate Limiting the CLoudFlare Workers


The CloudFlare provides a inhouse Rate Limiter. However, in order to use the CloudFlare Rate Limiter on Worker, we have to bind the worker functions to a domain (and add a route) first in order to configure and enable the rate limiting.

Currently, CloudFlare Rate Limiter charges like 2 cent per 10K requests, so maybe it is better to utilize this feature without managing the rate limiting ourselves.

A Simple Rate Limiter for CloudFlare (Serverless API) based on KV Stores


CloudFlare provides a KV Store (Key Value) which is a eventually consistent data storage. In order to use this, we have to first create a namespace in Workers/KV and then bind it to the worker function (under Workers/Settings/Variables). Assume we have binded the NAMESPACE to KV, then we can add the following Rate Limiter logics at the begining of handling the requests.

async function handleRequest(request) {
  let res;
  const MAX_REQUESTS = 60;
  const ip = request.headers.get("CF-Connecting-IP");

  let value = await KV.get(ip)
  if (value === null) {
    value = 1;
  }
  if (value >= MAX_REQUESTS) {
      res = new Response(null, {
        status: 429,
        statusText: 'Too Many Requests',
      });
      res.headers.set('Access-Control-Allow-Origin', '*');
      res.headers.set('Cache-Control', 'max-age=3');    
      return res;
  }

  try {    
    await KV.put(ip, parseInt(value) + 1, { expirationTtl: 61 });
  } catch (e) {
    // ignore
  }  
  // the main Worker API logics 
  // ...
}

However, there are limitations:

  • The expirationTtl parameter setting the key-value expires in seconds from now - should be set to at least 60 or more. This is due to that the CloudFlare KV will be propagated to edge servers no more than 60 seconds.
  • The KV supports unlimited reads and writes (different keys). For same keys, max 1 write per second. For Free Tier, there is a daily cap of 100K reads and 1000 writes. Therefore, we have to put the KV.put in a try-catch to ignore the failure especially if it is under Free Tier.
  • The KV is an event consistent storage, so the read may reflect to an old state. So it might not work precisely.
  • Since there is only 1 write per second, thus any MAX REQUESTS larger than 60 may not be actually hit.
  • We use the IP address as the keys (the buckets), so therefore might not work the best especially if an IP address is shared among many.

But, still, better than nothing. We can still apply the above simple rate limiter logics to avoid flood spam the CloudFlare Worker!

Reposted to Blog

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Thank you for reading ^^^^^^^^^^^^^^^

NEW! Following my Trail (Upvote or/and Downvote)

Follow me for topics of Algorithms, Blockchain and Cloud.
I am @justyy - a Steem Witness
https://steemyy.com

My contributions

Steem/Swap to USDT Swap

I also made this Super Easy/Handy Service to Convert your STEEM or SBD to USDT (TRC-20)

Delegation Service

Voting Power Considered in Voting Schema and Important Update of Delegation Service!

  • Delegate 1000 to justyy: Link
  • Delegate 5000 to justyy: Link
  • Delegate 10000 to justyy: Link

Support me

If you like my work, please:

  1. Delegate SP: https://steemyy.com/sp-delegate-form/?delegatee=justyy
  2. Vote @justyy as Witness: https://steemyy.com/witness-voting/?witness=justyy&action=approve
  3. Set @justyy as Proxy: https://steemyy.com/witness-voting/?witness=justyy&action=proxy
    Alternatively, you can vote witness or set proxy here: https://steemit.com/~witnesses

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE BLURT!