{"id":87,"date":"2026-07-18T00:00:00","date_gmt":"2026-07-18T00:00:00","guid":{"rendered":"http:\/\/\/var\/www\/html\/?p=87"},"modified":"2026-07-19T16:42:00","modified_gmt":"2026-07-19T16:42:00","slug":"nginx-rate-limit-exceeded-redirecting-requests-status-503-on-ubuntu-2004-lts","status":"publish","type":"post","link":"https:\/\/staging.butitworkedlocal.com\/?p=87","title":{"rendered":"Troubleshooting Nginx 503 Service Unavailable Due to Rate Limit Exceeded on Ubuntu 20.04 LTS"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>Resolve Nginx 503 errors caused by rate limiting. Learn to diagnose, adjust limit_req_zone, and optimize Nginx configuration on Ubuntu 20.04 LTS.<\/strong><\/p>\n\n\n<p>When your Nginx web server starts returning <code>HTTP 503 Service Unavailable<\/code> errors, especially during periods of high traffic or suspicious activity, it often indicates that Nginx&#039;s built-in rate limiting mechanism has been triggered. This guide delves into diagnosing and resolving &quot;Nginx rate limit exceeded&quot; issues, specifically on Ubuntu 20.04 LTS, ensuring your services remain available and performant under load.<\/p>\n\n<h3>Symptom &amp; Error Signature<\/h3>\n\n<p>Users attempting to access your web application will receive an <code>HTTP 503 Service Unavailable<\/code> response. The browser might display a generic Nginx 503 page or your custom error page, stating that the service is temporarily unavailable.<\/p>\n\n<p>Upon inspecting Nginx access logs (typically <code>\/var\/log\/nginx\/access.log<\/code>), you will observe numerous entries with a <code>503<\/code> status code for affected requests:<\/p>\n\n<pre><code class=\"language-nginx\">192.168.1.1 - - [18\/Jul\/2026:10:30:05 +0000] &quot;GET \/api\/data HTTP\/1.1&quot; 503 197 &quot;-&quot; &quot;Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/100.0.4896.75 Safari\/537.36&quot;\n192.168.1.2 - - [18\/Jul\/2026:10:30:06 +0000] &quot;POST \/submit HTTP\/1.1&quot; 503 197 &quot;-&quot; &quot;User-Agent: MyBot\/1.0&quot;<\/code><\/pre>\n\n<p>Crucially, the Nginx error log (typically <code>\/var\/log\/nginx\/error.log<\/code>) will contain specific messages indicating rate limiting:<\/p>\n\n<pre><code class=\"language-nginx\">2026\/07\/18 10:30:05 [error] 12345#12345: *67890 limiting requests, excess: 5.000 by zone &quot;api_rate_limit&quot;, client: 192.168.1.1, server: example.com, request: &quot;GET \/api\/data HTTP\/1.1&quot;, host: &quot;example.com&quot;\n2026\/07\/18 10:30:06 [error] 12346#12346: *67891 limiting requests, excess: 3.500 by zone &quot;api_rate_limit&quot;, client: 192.168.1.2, server: example.com, request: &quot;POST \/submit HTTP\/1.1&quot;, host: &quot;example.com&quot;, referrer: &quot;http:\/\/example.com\/&quot;<\/code><\/pre>\n\n<p>The key phrase here is <code>limiting requests, excess: X.XXX by zone &quot;your<em>zone<\/em>name&quot;<\/code>, which confirms that the Nginx <code>limit_req<\/code> module is actively rejecting or delaying requests.<\/p>\n\n<h3>Root Cause Analysis<\/h3>\n\n<p>The <code>HTTP 503 Service Unavailable<\/code> status, accompanied by &quot;limiting requests&quot; messages in the error log, directly points to Nginx&#039;s <code>limit_req<\/code> module. This module is designed to control the rate of requests for specific keys (typically client IP addresses) and is configured using two primary directives:<\/p>\n\n<ol><li> <strong><code>limit<em>req<\/em>zone<\/code><\/strong>: Defined in the <code>http<\/code> context, this directive creates a shared memory zone to store request states. It specifies:<\/li><li>    *   <strong>Key<\/strong>: The variable to track (e.g., <code>$binary<em>remote<\/em>addr<\/code> for client IP).<\/li><li>    *   <strong>Zone Name &amp; Size<\/strong>: A unique name for the zone and its memory allocation (e.g., <code>zone=api<em>rate<\/em>limit:10m<\/code>).<\/li><li>    *   <strong>Rate<\/strong>: The maximum average request rate allowed (e.g., <code>rate=5r\/s<\/code> for 5 requests per second, or <code>rate=300r\/m<\/code> for 300 requests per minute).<\/li><li> <strong><code>limit<em>req<\/code><\/strong>: Applied within <code>http<\/code>, <code>server<\/code>, or <code>location<\/code> contexts, this directive references a <code>limit<\/em>req_zone<\/code> and specifies:<\/li><li>    *   <strong>Zone Name<\/strong>: Which shared memory zone to use.<\/li><li>    *   <strong>Burst<\/strong>: How many requests a client can make in excess of the defined rate without being immediately rejected. These excess requests are queued and processed with a delay.<\/li><li>    <em>   <strong>Nodelay<\/strong>: An optional parameter that, if present, tells Nginx to process requests without delay once the burst limit is reached, potentially leading to server overload but avoiding client delays. If <code>nodelay<\/code> is <\/em>not* used, requests exceeding the rate but within the burst limit are delayed. If <code>burst<\/code> itself is exceeded (or <code>nodelay<\/code> is present and the rate is exceeded), Nginx returns a 503 (or 500 depending on <code>limit<em>req<\/em>status<\/code>).<\/li><\/ol>\n\n<p><strong>Common Scenarios Leading to Rate Limit Exceedance:<\/strong><\/p>\n\n<ul><li>  <strong>Legitimate High Traffic:<\/strong> A sudden surge in user activity, viral content, or successful marketing campaigns can push legitimate traffic beyond the configured limits.<\/li><li>  <strong>Malicious Activity\/DDoS:<\/strong> Bots, scrapers, or Distributed Denial of Service (DDoS) attacks can generate an extremely high volume of requests, intentionally triggering rate limits.<\/li><li>  <strong>Misconfigured Clients\/Scripts:<\/strong> Automated scripts, monitoring tools, or even poorly implemented AJAX calls can make excessive requests, especially if there&#039;s a bug causing a loop.<\/li><li>  <strong>Overly Aggressive Configuration:<\/strong> The <code>limit<em>req<\/em>zone<\/code> and <code>limit_req<\/code> parameters might be set too low for the actual expected traffic patterns of your application.<\/li><\/ul>\n\n<h3>Step-by-Step Resolution<\/h3>\n\n<h4>1. Identify the Active Nginx Rate Limit Configuration<\/h4>\n\n<p>Your first step is to locate where <code>limit<em>req<\/em>zone<\/code> and <code>limit_req<\/code> are defined in your Nginx configuration. Nginx configurations on Ubuntu 20.04 LTS are typically found in <code>\/etc\/nginx\/nginx.conf<\/code> and files included from <code>conf.d<\/code> or <code>sites-enabled<\/code>.<\/p>\n\n<pre><code class=\"language-bash\"># Search for limit_req_zone and limit_req directives across Nginx configuration files\ngrep -R &quot;limit_req_zone&quot; \/etc\/nginx\/\ngrep -R &quot;limit_req&quot; \/etc\/nginx\/<\/code><\/pre>\n\n<p>You&#039;ll likely find something similar to this structure:<\/p>\n\n<p><strong><code>\/etc\/nginx\/nginx.conf<\/code> (or an included file like <code>\/etc\/nginx\/conf.d\/ratelimit.conf<\/code>):<\/strong><\/p>\n\n<pre><code class=\"language-nginx\">http {\n    # ... other http settings ...\n    # Defines a 10MB zone named &#039;api_rate_limit&#039; tracking client IPs, allowing 5 requests\/second\n    limit_req_zone $binary_remote_addr zone=api_rate_limit:10m rate=5r\/s;\n    # ...\n}<\/code><\/pre>\n\n<p><strong><code>\/etc\/nginx\/sites-enabled\/your_site.conf<\/code> (or within a location block inside a server config):<\/strong><\/p>\n\n<pre><code class=\"language-nginx\">server {\n    listen 80;<\/code><\/pre>\n\n<p>location \/api\/ {\n        # Applies the &#039;api<em>rate<\/em>limit&#039; zone, allowing a burst of 10 requests, with no delay\n        limit<em>req zone=api<\/em>rate_limit burst=10 nodelay;\n        # &#8230; proxy_pass or root directives &#8230;\n    }<\/p>\n\n<p>location \/admin\/ {\n        # Potentially a different limit for admin panel\n        limit<em>req zone=api<\/em>rate_limit burst=5 nodelay; \n        # &#8230;\n    }\n}\n<code><\/code>`<\/p>\n\n<blockquote><p>[!IMPORTANT]\nNote the <code>zone=<\/code> name (e.g., <code>api<em>rate<\/em>limit<\/code>). This name is crucial as it links the <code>limit<em>req<\/em>zone<\/code> definition (where the rate and zone size are set) to the <code>limit_req<\/code> application (where burst and nodelay are set).<\/p><\/blockquote>\n\n<h4>2. Analyze Nginx Error Logs for Specifics<\/h4>\n\n<p>Re-examine <code>\/var\/log\/nginx\/error.log<\/code> for recent <code>limiting requests<\/code> messages. This will confirm the specific zone being hit, the client IP addresses that are exceeding the limit, and the <code>excess<\/code> value.<\/p>\n\n<pre><code class=\"language-bash\">sudo tail -f \/var\/log\/nginx\/error.log | grep &quot;limiting requests&quot;<\/code><\/pre>\n\n<p>Look for the <code>excess: X.XXX<\/code> value. A high <code>excess<\/code> value indicates that requests are coming in much faster than the configured rate and burst limits. This helps you understand the magnitude of the problem.<\/p>\n\n<h4>3. Adjust <code>limit<em>req<\/em>zone<\/code> Parameters (Rate and Burst)<\/h4>\n\n<p>This is the most common and direct resolution: increasing the allowed request rate or burst capacity to accommodate legitimate traffic.<\/p>\n\n<p>Edit the Nginx configuration file where your <code>limit<em>req<\/em>zone<\/code> is defined (e.g., <code>\/etc\/nginx\/nginx.conf<\/code> or a custom <code>.conf<\/code> in <code>conf.d<\/code>).<\/p>\n\n<pre><code class=\"language-bash\">sudo nano \/etc\/nginx\/nginx.conf<\/code><\/pre>\n\n<p><strong>Understanding the Parameters to Adjust:<\/strong><\/p>\n\n<ul><li>  <strong><code>rate<\/code><\/strong>: Defines the average request processing rate Nginx attempts to maintain.<\/li><li>  *   <code>rate=5r\/s<\/code>: Allows 5 requests per second.<\/li><li>  *   <code>rate=300r\/m<\/code>: Allows 300 requests per minute (equivalent to 5r\/s).<\/li><li>  *   <strong>Recommendation:<\/strong> If you suspect legitimate traffic is being blocked, consider increasing the rate. A good starting point is to double the current rate (e.g., from <code>5r\/s<\/code> to <code>10r\/s<\/code>) and monitor.<\/li><li>  <strong><code>burst<\/code><\/strong>: Specifies how many requests a client can send <em>in excess<\/em> of the defined <code>rate<\/code> before Nginx returns a 503. These excess requests are put into a queue and processed as capacity becomes available.<\/li><li>  *   <code>burst=10<\/code>: Allows 10 requests to be queued.<\/li><li>  *   <strong>Recommendation:<\/strong> Increase this value to accommodate momentary spikes in traffic. A higher <code>burst<\/code> allows more flexibility and reduces 503 errors during brief peaks but consumes more shared memory for the queue.<\/li><li>  <strong><code>zone<\/code> size<\/strong>: The memory allocated for the shared memory zone (e.g., <code>zone=api<em>rate<\/em>limit:10m<\/code>). Each state for an IP address takes about 32 bytes. <code>10m<\/code> can store ~320,000 active IP addresses. If you significantly increase the burst or expect many unique IPs, you might need to increase the zone size.<\/li><\/ul>\n\n<p><strong>Example Modification:<\/strong><\/p>\n\n<p>Original (potentially too restrictive):<\/p>\n\n<pre><code class=\"language-nginx\"># In http block:<\/code><\/pre>\n\n<h1>In server\/location:\nlimit<em>req zone=api<\/em>rate_limit burst=10 nodelay; \n<code><\/code>`<\/h1>\n\n<p>Modified (more permissive):<\/p>\n\n<pre><code class=\"language-nginx\"># In http block:\n# Increased rate to 10 requests\/sec and zone size to 20MB<\/code><\/pre>\n\n<h1>In server\/location:\n# Increased burst to 20 requests\nlimit<em>req zone=api<\/em>rate_limit burst=20 nodelay; \n<code><\/code>`<\/h1>\n\n<blockquote><p>[!WARNING]\nIncreasing <code>rate<\/code> and <code>burst<\/code> too aggressively without understanding your server&#039;s underlying capacity (CPU, memory, backend application limits) can lead to backend overload and system instability. Always monitor server resource usage after making changes.<\/p><\/blockquote>\n\n<h4>4. Consider Whitelisting Specific IPs<\/h4>\n\n<p>If you have specific IP addresses (e.g., your own monitoring systems, trusted partners, or internal tools) that legitimately need to make high volumes of requests without being rate-limited, you can whitelist them using the <code>geo<\/code> or <code>map<\/code> directives.<\/p>\n\n<p><strong>Using <code>geo<\/code> directive (in <code>http<\/code> block):<\/strong><\/p>\n\n<pre><code class=\"language-nginx\">http {\n    # ...\n    # Define a variable &#039;$whitelist&#039; that is 0 for whitelisted IPs, and 1 for others\n    geo $whitelist {\n        default 1;           # By default, enable rate limiting\n        127.0.0.1 0;         # Whitelist localhost\n        192.168.1.0\/24 0;    # Whitelist a subnet (e.g., internal network)\n        203.0.113.10 0;      # Whitelist a specific public IP<\/code><\/pre>\n\n<h1>Now, define your rate limit zone, potentially referencing the whitelist variable\n    limit<em>req<\/em>zone $binary<em>remote<\/em>addr zone=api<em>rate<\/em>limit:10m rate=5r\/s;\n}\n<code><\/code>`<\/h1>\n\n<p>Then, in your <code>server<\/code> or <code>location<\/code> block, apply the <code>limit_req<\/code> only if the client is not whitelisted:<\/p>\n\n<pre><code class=\"language-nginx\">server {\n    # ...\n    location \/api\/ {\n        if ($whitelist = 1) { # Only apply rate limit if client is not whitelisted\n            limit_req zone=api_rate_limit burst=10 nodelay;\n        }\n        # ... Other directives for \/api\/\n    }\n}<\/code><\/pre>\n\n<h4>5. Test Nginx Configuration and Reload<\/h4>\n\n<p>After making any changes to Nginx configuration files, it is <strong>critical<\/strong> to test the syntax before reloading. This prevents Nginx from failing to start due to misconfigurations.<\/p>\n\n<pre><code class=\"language-bash\">sudo nginx -t<\/code><\/pre>\n\n<p>If the test is successful, you&#039;ll see messages like:<\/p>\n\n<pre><code class=\"language-\">nginx: the configuration file \/etc\/nginx\/nginx.conf syntax is ok\nnginx: configuration file \/etc\/nginx\/nginx.conf test is successful<\/code><\/pre>\n\n<p>Then, reload Nginx to apply the changes gracefully:<\/p>\n\n<pre><code class=\"language-bash\">sudo systemctl reload nginx<\/code><\/pre>\n\n<blockquote><p>[!IMPORTANT]\nAlways use <code>sudo systemctl reload nginx<\/code> instead of <code>sudo systemctl restart nginx<\/code> in production environments. <code>reload<\/code> allows Nginx to gracefully shut down old worker processes and start new ones with the updated configuration, minimizing downtime by keeping existing connections active during the transition.<\/p><\/blockquote>\n\n<h4>6. Monitor and Iterate<\/h4>\n\n<p>After applying the changes, it&#039;s crucial to continuously monitor your Nginx error logs, access logs, and overall server resource utilization.<\/p>\n\n<ul><li>  <strong>Check <code>error.log<\/code><\/strong>: Ensure <code>limiting requests<\/code> messages have significantly reduced or disappeared for legitimate traffic patterns.<\/li><li>  <strong>Check <code>access.log<\/code><\/strong>: Verify that <code>503<\/code> responses are no longer being served by Nginx for requests that should be allowed.<\/li><li>  <strong>System Monitoring<\/strong>: Use tools like <code>htop<\/code>, <code>netdata<\/code>, <code>atop<\/code>, or integrated solutions like <code>Prometheus\/Grafana<\/code> to observe CPU, memory, network I\/O, and active connection counts. If these resources spike dangerously after increasing limits, your backend application or database might be the actual bottleneck, not just Nginx&#039;s rate limit.<\/li><\/ul>\n\n<p>You may need to iterate on these changes, gradually adjusting <code>rate<\/code> and <code>burst<\/code> until you find a balanced configuration that protects your server resources while allowing legitimate traffic to flow freely.<\/p>\n\n<h4>7. Consider External Solutions (Advanced)<\/h4>\n\n<p>For high-volume websites or those frequently targeted by sophisticated DDoS attacks, Nginx&#039;s built-in rate limiting might not be sufficient on its own. Consider implementing additional layers of protection:<\/p>\n\n<ul><li>  <strong>Content Delivery Networks (CDNs) with WAF<\/strong>: Services like Cloudflare, Akamai, or AWS CloudFront with Web Application Firewall (WAF) capabilities can absorb and filter malicious traffic and bot requests before they ever reach your Nginx server, significantly reducing the load.<\/li><li>  <strong>Dedicated Web Application Firewalls (WAFs)<\/strong>: Standalone or cloud-based WAFs can provide more sophisticated rule sets, behavioral analysis, and threat intelligence for advanced attack detection and mitigation.<\/li><li>  <strong>Fail2Ban<\/strong>: While Nginx rate limiting deals with requests per second, Fail2Ban can be configured to parse Nginx logs and automatically ban (at the firewall level) IP addresses that repeatedly trigger rate limit errors or other suspicious activities, effectively blocking persistent attackers.<\/li><\/ul>\n\n<p>By carefully diagnosing the cause and making informed adjustments to your Nginx rate limiting configuration, you can effectively manage traffic spikes and protect your web services from overload on Ubuntu 20.04 LTS.<\/p>","protected":false},"excerpt":{"rendered":"<p>Resolve Nginx 503 errors caused by rate limiting. Learn to diagnose, adjust limit_req_zone, and optimize Nginx configuration on Ubuntu 20.04 LTS.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[223,40,55,91,34,18],"class_list":["post-87","post","type-post","status-publish","format-standard","hentry","category-web-server","tag-503-error","tag-devops","tag-nginx","tag-rate-limit","tag-sysadmin","tag-ubuntu-20-04"],"_links":{"self":[{"href":"https:\/\/staging.butitworkedlocal.com\/index.php?rest_route=\/wp\/v2\/posts\/87","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/staging.butitworkedlocal.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/staging.butitworkedlocal.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/staging.butitworkedlocal.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/staging.butitworkedlocal.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=87"}],"version-history":[{"count":1,"href":"https:\/\/staging.butitworkedlocal.com\/index.php?rest_route=\/wp\/v2\/posts\/87\/revisions"}],"predecessor-version":[{"id":206,"href":"https:\/\/staging.butitworkedlocal.com\/index.php?rest_route=\/wp\/v2\/posts\/87\/revisions\/206"}],"wp:attachment":[{"href":"https:\/\/staging.butitworkedlocal.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=87"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/staging.butitworkedlocal.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=87"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/staging.butitworkedlocal.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=87"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}