1"use strict";(self.webpackChunkblog=self.webpackChunkblog||[]).push([[1939],{622:function(e,t,n){n.r(t),n.d(t,{default:function(){return f}});var a=n(8453),r=n(6540);function l(e){const t=Object.assign({h1:"h1",p:"p",hr:"hr",h2:"h2",ul:"ul",li:"li",strong:"strong",h3:"h3",br:"br",span:"span"},(0,a.RP)(),e.components);return r.createElement(r.Fragment,null,r.createElement(t.h1,null,"Mastering Rate Limiting Patterns at Scale: Striking a Balance between Fairness, Speed, and Protection"),"\n",r.createElement(t.p,null,"Rate limiting is a straightforward task in a prototype application. However, enforcing it in a production environment across multiple data centers, numerous microservices, various API gateways, and billions of daily requests can be an uphill battle."),"\n",r.createElement(t.p,null,"In a large-scale environment, rate limiting transforms into an intricate system that influences caching, replication, performance, storage, real-time enforcement, and developer experience."),"\n",r.createElement(t.p,null,"This article will delve into the architectural patterns employed to build scalable, fair, and resilient rate limiting for APIs that operate globally and serve millions of developers. We'll dissect algorithms, data stores, enforcement strategies, and lessons learned from the frontlines."),"\n",r.createElement(t.hr),"\n",r.createElement(t.h2,null,"Why Scalable Rate Limiting Is Challenging"),"\n",r.createElement(t.p,null,"In theory, rate limiting seems simple: set a quota, track usage, and enforce limits. However, in practice, it is fraught with complexities:"),"\n",r.createElement(t.ul,null,"\n",r.createElement(t.li,null,"Traffic patterns are unpredictable and can often be bursty."),"\n",r.createElement(t.li,null,"System clocks are inconsistent across different servers and data centers."),"\n",r.createElement(t.li,null,"Requests may arrive at different edge nodes."),"\n",r.createElement(t.li,null,"Some limits are applied per-user, others per-IP or per-resource."),"\n",r.createElement(t.li,null,"You need global visibility for enforcement, but local performance for speed."),"\n"),"\n",r.createElement(t.p,null,"A naive in-memory counter is insufficient for these challenges. Scalable rate limiting needs to be ",r.createElement(t.strong,null,"fast, accurate, fair, and distributed"),"."),"\n",r.createElement(t.hr),"\n",r.createElement(t.h2,null,"Core Strategies and Concepts"),"\n",r.createElement(t.h3,null,"Token Bucket Algorithm"),"\n",r.createElement(t.p,null,"In the Token Bucket algorithm, each entity (user, API key, IP) is assigned a âbucketâ of tokens. Each request consumes tokens, which are replenished at a fixed rate."),"\n",r.createElement(t.ul,null,"\n",r.createElement(t.li,null,"It permits short bursts above the average rate, providing flexibility."),"\n",r.createElement(t.li,null,"It smooths traffic without a hard cutoff, preventing sudden drops in service."),"\n",r.createElement(t.li,null,"It is relatively simple to implement in Redis or in-memory databases."),"\n"),"\n",r.createElement(t.p,null,"Prominent platforms like Stripe, AWS, and GitHub employ this algorithm."),"\n",r.createElement(t.hr),"\n",r.createElement(t.h3,null,"Leaky Bucket Algorithm"),"\n",r.createElement(t.p,null,"The Leaky Bucket algorithm processes requests at a constant rate. If the incoming request rate exceeds the processing capacity, excess requests are queued or discarded."),"\n",r.createElement(t.ul,null,"\n",r.createElement(t.li,null,"It is ideal for output shaping, ensuring a steady flow of outgoing requests."),"\n",r.createElement(t.li,null,"It smooths unpredictable input, providing a buffer against spikes."),"\n",r.createElement(t.li,null,"It requires a queue infrastructure to handle excess requests."),"\n"),"\n",r.createElement(t.p,null,"This algorithm is effective when you want a deterministic flow, such as sending emails."),"\n",r.createElement(t.hr),"\n",r.createElement(t.h3,null,"Fixed and Sliding Windows"),"\n",r.createElement(t.p,null,r.createElement(t.strong,null,"Fixed window"),": The request count is reset every 'N' seconds, providing a clean slate.",r.createElement(t.br),"\n",r.createElement(t.strong,null,"Sliding window"),": The request count is tracked over the last 'N' seconds, providing a moving perspective."),"\n",r.createElement(t.p,null,"Sliding windows are fairer and smoother but require more complex implementation as they need timestamp tracking or window bucketing."),"\n",r.createElement(t.p,null,"These strategies are especially useful for controlling login attempts, burst traffic, and polling APIs."),"\n",r.createElement(t.hr),"\n",r.createElement(t.h3,null,"Distributed Rate Limiting"),"\n",r.createElement(t.p,null,"Enforcing a global limit across distributed nodes presents a unique challenge."),"\n",r.createElement(t.p,null,r.createElement(t.strong,null,"Strategies:")),"\n",r.createElement(t.ul,null,"\n",r.createElement(t.li,null,"Redis-based counters (with TTL, Lua scripts)"),"\n",r.createElement(t.li,null,"DynamoDB or Bigtable with atomic counters"),"\n",r.createElement(t.li,null,"Memcached with sliding windows"),"\n",r.createElement(t.li,null,"Central limiter service communicating via gRPC"),"\n",r.createElement(t.li,null,"In-memory counters with edge synchronization (best-effort approach)"),"\n"),"\n",r.createElement(t.hr),"\n",r.createElement(t.h2,null,"Real-World Systems"),"\n",r.createElement(t.h3,null,"GitHub"),"\n",r.createElement(t.ul,null,"\n",r.createElement(t.li,null,"The REST API operates with hourly quotas (5000 requests/hour per user/token)."),"\n",r.createElement(t.li,null,"The GraphQL API uses a query cost budget (1000 points/hour)."),"\n",r.createElement(t.li,null,"Enforcement is handled at the edge, and tracking is performed via tokens."),"\n",r.createElement(t.li,null,"Headers like ",r.createElement(t.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">
1X-RateLimit-Limit</code>'}}),", ",r.createElement(t.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Remaining</code>'}}),", and ",r.createElement(t.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Reset</code>'}})," provide visibility for clients."),"\n"),"\n",r.createElement(t.h3,null,"Stripe"),"\n",r.createElement(t.ul,null,"\n",r.createElement(t.li,null,"Stripe differentiates between public and secret keys."),"\n",r.createElement(t.li,null,"Bucket-based limits are set per endpoint class (read, write, auth)."),"\n",r.createElement(t.li,null,"Quotas are reset every second/minute depending on the key type."),"\n",r.createElement(t.li,null,"Monitoring tools are used to identify and flag patterns of overuse."),"\n"),"\n",r.createElement(t.h3,null,"Cloudflare"),"\n",r.createElement(t.ul,null,"\n",r.createElement(t.li,null,"Cloudflare rate limits based on IP, user agent, and route."),"\n",r.createElement(t.li,null,"Tracking is performed regionally or by Point of Presence (PoP)."),"\n",r.createElement(t.li,null,"Rules are exposed as configurations in the dashboard."),"\n",r.createElement(t.li,null,"Excessive requests are dropped with configurable response headers."),"\n"),"\n",r.createElement(t.hr),"\n",r.createElement(t.h2,null,"Advanced Features"),"\n",r.createElement(t.ul,null,"\n",r.createElement(t.li,null,r.createElement(t.strong,null,"Overdraft window"),": Provides temporary forgiveness for minor bursts."),"\n",r.createElement(t.li,null,r.createElement(t.strong,null,"Quota pooling"),": Teams share a limit instead of individual per-user quotas."),"\n",r.createElement(t.li,null,r.createElement(t.strong,null,"Dynamic quotas"),": These are adjusted based on the plan tier or usage growth."),"\n",r.createElement(t.li,null,r.createElement(t.strong,null,"Billing hooks"),": Overstepping the limit triggers upsell or pricing actions."),"\n",r.createElement(t.li,null,r.createElement(t.strong,null,"Audit logging"),": This is crucial for abuse tracking and customer support."),"\n"),"\n",r.createElement(t.hr),"\n",r.createElement(t.h2,null,"Caching and Syncing Limits"),"\n",r.createElement(t.ul,null,"\n",r.createElement(t.li,null,"Redis or Memcached can be used for fast access."),"\n",r.createElement(t.li,null,"Time-to-live (TTL) ensures automatic quota reset."),"\n",r.createElement(t.li,null,"Lua scripts can be used for atomic operations, such as:","\n",r.createElement(t.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="lua"><pre style="counter-reset: linenumber NaN" class="language-lua line-numbers"><code class="language-lua"><span class="token keyword">if</span> redis<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token string">"GET"</span><span class="token punctuation">,</span> key<span class="token punctuation">)</span> <span class="token operator">></span> limit <span class="token keyword">then</span> <span class="token keyword">return</span> <span class="token number">0</span> <span class="token keyword">end</span></code><span aria-hidden="true" class="line-numbers-rows" style="white-space: normal; width: auto; left: 0;"><span></span></span></pre></div>'}}),"\n"),"\n",r.createElement(t.li,null,"At scale, state can be replicated using pub/sub or stream logs."),"\n",r.createElement(t.li,null,"For precise control, counters can be sharded by region or time window."),"\n"),"\n",r.createElement(t.hr),"\n",r.createElement(t.h2,null,"Exposing Rate Info to Clients"),"\n",r.createElement(t.p,null,"To provide transparency, always return structured rate metadata in HTTP headers:"),"\n",r.createElement(t.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="http"><pre style="counter-reset: linenumber NaN" class="language-http line-numbers"><code class="language-http"><span class="token response-status"><span class="token http-version property">HTTP/1.1</span> <span class="token status-code number">429</span> <span class="token reason-phrase string">Too Many Requests</span></span>\n<span class="token header"><span class="token header-name keyword">
1X-RateLimit-Limit</span><span class="token punctuation">:</span> <span class="token header-value">1000</span></span>\n<span class="token header"><span class="token header-name keyword">X-RateLimit-Remaining</span><span class="token punctuation">:</span> <span class="token header-value">0</span></span>\n<span class="token header"><span class="token header-name keyword">X-RateLimit-Reset</span><span class="token punctuation">:</span> <span class="token header-value">1710101600</span></span>\n<span class="token header"><span class="token header-name keyword">Retry-After</span><span class="token punctuation">:</span> <span class="token header-value">30</span></span></code><span aria-hidden="true" class="line-numbers-rows" style="white-space: normal; width: auto; left: 0;"><span></span><span></span><span></span><span></span><span></span></span></pre></div>'}}),"\n",r.createElement(t.p,null,"Or in a GraphQL response:"),"\n",r.createElement(t.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="json"><pre style="counter-reset: linenumber NaN" class="language-json line-numbers"><code class="language-json"><span class="token punctuation">{</span>\n <span class="token property">"rateLimit"</span><span class="token operator">:</span> <span class="token punctuation">{</span>\n <span class="token property">"limit"</span><span class="token operator">:</span> <span class="token number">1000</span><span class="token punctuation">,</span>\n <span class="token property">"remaining"</span><span class="token operator">:</span> <span class="token number">23</span><span class="token punctuation">,</span>\n <span class="token property">"resetAt"</span><span class="token operator">:</span> <span class="token string">"2025-04-19T10:45:00Z"</span>\n <span class="token punctuation">}</span>\n<span class="token punctuation">}</span></code><span aria-hidden="true" class="line-numbers-rows" style="white-space: normal; width: auto; left: 0;"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre></div>'}}),"\n",r.createElement(t.hr),"\n",r.createElement(t.h2,null,"Best Practices"),"\n",r.createElement(t.ul,null,"\n",r.createElement(t.li,null,"Bucket by user, key, IP, or organization, depending on the context."),"\n",r.createElement(t.li,null,"Donât block on synchronization, degrade gracefully."),"\n",r.createElement(t.li,null,"Use exponential backoff for retries to prevent storming the server."),"\n",r.createElement(t.li,null,"Warn clients if they are nearing their limits."),"\n",r.createElement(t.li,null,"Group traffic by category (read/write/mutation/stream) for better control."),"\n"),"\n",r.createElement(t.hr),"\n",r.createElement(t.h2,null,"Anti-Patterns"),"\n",r.createElement(t.ul,null,"\n",r.createElement(t.li,null,"Applying one-size-fits-all limits."),"\n",r.createElement(t.li,null,"Forgetting to log denials or limit hits."),"\n",r.createElement(t.li,null,"Storing limits in relational databases (they're too slow!)."),"\n",r.createElement(t.li,null,"Hard-coding limits in application logic."),"\n",r.createElement(t.li,null,"Using HTTP 200 status code for blocked requests (it's confusing!)."),"\n"),"\n",r.createElement(t.hr),"\n",r.createElement(t.h2,null,"Monitoring and Tuning"),"\n",r.createElement(t.p,null,"Track:"),"\n",r.createElement(t.ul,null,"\n",r.createElement(t.li,null,"Bucket fill/drain rates to identify usage patterns."),"\n",r.createElement(t.li,null,"429 responses to quantify rate limiting."),"\n",r.createElement(t.li,null,"Abuse patterns (e.g., brute-force login) to enhance security."),"\n",r.createElement(t.li,null,"Top violators (per key or IP) to mitigate potential threats."),"\n",r.createElement(t.li,null,"False positives (rate-limited legitimate traffic) to adjust thresholds."),"\n"),"\n",r.createElement(t.p,null,"When possible, provide dashboards to internal teams and external developers for visibility and control."),"\n",r.createElement(t.hr),"\n",r.createElement(t.h2,null,"Conclusion: Rate Limits Are Policy, Not Just Code"),"\n",r.createElement(t.p,null,"Rate limiting is more than a gate
1keeper; it is a statement about resource capacity, fairness, and responsibility. It ensures one user doesnât monopolize resources at the expense of others. It sets clear expectations for clients about usage limits. Moreover, it protects your business infrastructure as much as your backend."),"\n",r.createElement(t.p,null,"At scale, rate limiting evolves into a distributed, observable, policy-driven platform, not a mere function call."),"\n",r.createElement(t.p,null,"So, build it like it matters."),"\n",r.createElement(t.p,null,"Because at scale, it absolutely does."))}var s=function(e){void 0===e&&(e={});const{wrapper:t}=Object.assign({},(0,a.RP)(),e.components);return t?r.createElement(t,e,r.createElement(l,e)):l(e)},i=n(4794),o=n(8154),c=n(9807),m=n(7772),u=n(984),p=n(5154),d=n(1166),g=n(893);const h={MermaidDiagram:p.A,EmailOff:d.A,pre:e=>r.createElement("div",Object.assign({},e,{className:"overflow-auto"})),iframe:e=>r.createElement("div",{className:"aspect-video"},r.createElement("iframe",Object.assign({},e,{className:"w-full h-full rounded-lg"})))},E=e=>{let{data:t,children:n}=e;if(!t||!t.mdx)return r.createElement(c.A,null,r.createElement("div",{className:"max-w-4xl mx-auto px-4 py-8"},r.createElement("h1",{className:"text-4xl font-bold mb-8"},"Post not found"),r.createElement("p",null,"Sorry, the post you're looking for doesn't exist.")));const{frontmatter:l,fields:s}=t.mdx,{title:p,publishedAt:d,publishedAtISO:E,summary:f,label:y,tags:b}=l,{readingTimeMinutes:k}=s,{siteUrl:v}=t.site.siteMetadata,x=`${v}${s.slug}`,w=s.slug.split("/").at(-2)||"",N=`https://bx.ehosseini.info/?text=${w}&complexity=6&format=webp&width=1200&height=675&title=${encodeURIComponent(p)}&avatar=1`,I=`https://bx.ehosseini.info/?text=${w}&complexity=6&format=webp&width=1200&height=675&align=bottom`,T=t.relatedPosts.nodes.filter((e=>e.fields.slug!==s.slug)).slice(0,3),R={"@type":"BlogPosting",headline:p,datePublished:E,dateModified:E,description:f,author:{"@id":g.eo},publisher:{"@id":g.eo},mainEntityOfPage:x,keywords:null==b?void 0:b.join(", "),image:N,url:x};return r.createElement(c.A,{schemaExtra:[R]},r.createElement(o.m,null,r.createElement("title",null,p," | ",t.site.siteMetadata.title),r.createElement("meta",{name:"description",content:f}),r.createElement("meta",{name:"keywords",content:b.join(", ")}),r.createElement("meta",{property:"og:type",content:"article"}),r.createElement("meta",{property:"og:url",content:x}),r.createElement("meta",{property:"og:title",content:p}),r.createElement("meta",{property:"og:description",content:f}),r.createElement("meta",{property:"og:image",content:N}),r.createElement("meta",{property:"og:image:secure_url",content:N}),r.createElement("meta",{property:"og:image:alt",content:p}),r.createElement("meta",{property:"og:image:width",content:"1200"}),r.createElement("meta",{property:"og:image:height",content:"675"}),r.createElement("meta",{property:"og:site_name",content:t.site.siteMetadata.title}),r.createElement("meta",{property:"article:published_time",content:E}),r.createElement("meta",{name:"twitter:card",content:"summary_large_image"}),r.createElement("meta",{name:"twitter:site",content:g._l.xHandle}),r.createElement("meta",{name:"twitter:creator",content:g._l.xHandle}),r.createElement("meta",{name:"twitter:title",content:p}),r.createElement("meta",{name:"twitter:description",content:f}),r.createElement("meta",{name:"twitter:image",content:N}),r.createElement("meta",{name:"twitter:image:alt",content:p}),r.createElement("link",{rel:"canonical",href:x})),r.createElement("div",{className:"relative w-full"},r.createElement("div",{className:"post-hero-bg-layer absolute inset-0 z-0",style:{backgroundImage:`url(${I})`,backgroundSize:"cover",backgroundRepeat:"no-repeat",backgroundPosition:"center"},"aria-hidden":"true"}),r.createElement("header",{className:"relative z-10 mb-8 max-w-3xl mx-auto px-4 pt-8 pb-4"},r.createElement("div",{className:"flex flex-col sm:flex-row sm:items-center gap-3 text-sm text-gray-900 mb-4"},r.createElement("time",{className:"flex items-center"},r.createElement("svg",{className:"w-4 h-4 mr-2",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"})),d),r.createElement("span",{className:"flex items-center"},r.createElement("svg",{className:"w-4 h-4 mr-2",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"})),k," min read"),y&&r.createElement("span",{className:"flex items-center"},r.createElement("svg",{className:"w-4 h-4 mr-2",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"})),y)),r.createElement("div",{className:"mb-6"},r.createElement("h1",{className:"text-3xl font-playfair font-medium text-gray-900 leading-tight mb-4"},p)),r.createElement("div",{className:"mb-6"},r.createElement("p",{className:"text-md leading-relaxed mb-4 text-gray-900"},f),r.createElement("div",{className:"flex flex-wrap gap-2"},null==b?void 0:b.map((e=>r.createElement(m.A,{key:e,tag:e,className:"text-sm"}))))))),r.createElement("article",{className:"max-w-3xl mx-auto px-4 pyb-8"},r.createElement("div",{className:""},r.createElement("div",{className:"flex items-center justify-between mb-4"},r.createElement("div",null),r.createElement("div",{className:"flex space-x-3"},r.createElement("a",{href:`https://twitter.com/intent/tweet?text=${encodeURIComponent(p)}&url=${encodeURIComponent(x)}&hashtags=${encodeURIComponent(b.join(", "))}&image=${encodeURIComponent(I)}`,target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center px-3 py-1.5 rounded-full text-sm font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"},r.createElement("svg",{className:"w-4 h-4 mr-1.5",fill:"currentColor",viewBox:"0 0 24 24"},r.createElement("path",{d:"M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84"})),"Share"),r.createElement("a",{href:`https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(x)}&title=${encodeURIComponent(p)}&summary=${encodeURIComponent(f)}&image=${encodeURIComponent(I)}`,target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center px-3 py-1.5 rounded-full text-sm font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"},r.createElement("svg",{className:"w-4 h-4 mr-1.5",fill:"currentColor",viewBox:"0 0 24 24"},r.createElement("path",{d:"M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"})),"Share"),r.createElement("button",{onClick:()=>{navigator.clipboard.writeText(x)},className:"inline-flex items-center px-3 py-1.5 rounded-full text-sm font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"},r.createElement("svg",{className:"w-4 h-4 mr-1.5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"})),"Copy Link")))),r.createElement("div",{className:"prose prose-md max-w-none prose-headings:font-playfair prose-headings:font-medium prose-a:text-blue-600 dar
1k:prose-a:text-blue-400 prose-a:no-underline hover:prose-a:text-blue-800 dark:hover:prose-a:text-blue-300 prose-img:rounded-lg prose-img:shadow-md dark:prose-invert dark:prose-headings:text-gray-100 dark:prose-p:text-gray-200 dark:prose-strong:text-gray-100 dark:prose-code:text-gray-200 dark:prose-pre:bg-gray-800 dark:prose-blockquote:text-gray-300 dark:prose-li:text-gray-200 dark:prose-td:text-gray-200 dark:prose-th:text-gray-200"},r.createElement(a.xA,{components:h},n)),r.createElement("div",{className:"mt-16 pt-8 border-t border-gray-200 dark:border-gray-700"},r.createElement(u.A,null)),T.length>0&&r.createElement("div",{className:"mt-16 pt-8 border-t border-gray-200 dark:border-gray-700"},r.createElement("h2",{className:"text-2xl font-playfair font-medium text-gray-900 dark:text-gray-100 mb-6"},"Related Posts"),r.createElement("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-6"},T.map((e=>r.createElement(i.Link,{key:e.id,to:e.fields.slug,className:"block bg-white dark:bg-gray-800 p-4 rounded-lg shadow-sm hover:shadow-md transition-shadow"},r.createElement("h3",{className:"text-lg font-playfair font-medium text-gray-900 dark:text-gray-100 mb-2"},e.frontmatter.title),r.createElement("div",{className:"flex items-center text-sm text-gray-500 dark:text-gray-400 mb-2"},r.createElement("span",null,e.frontmatter.publishedAt)),r.createElement("p",{className:"text-gray-600 dark:text-gray-300 text-sm line-clamp-2"},e.frontmatter.summary),r.createElement("div",{className:"mt-3 flex flex-wrap gap-1"},e.frontmatter.tags.map((e=>r.createElement(m.A,{key:e,tag:e,className:"text-xs"})))))))))))};function f(e){return r.createElement(E,e,r.createElement(s,e))}}}]); 2//# sourceMappingURL=component---src-templates-post-tsx-content-file-path-src-posts-rate-limiting-patterns-mdx-7e497aa818e8a332e1a0.js.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.