> ## Documentation Index
> Fetch the complete documentation index at: https://www.qovery.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# How to Setup Rate Limit on Services

> Configure rate limiting for services using NGINX

This guide covers implementing rate limits on Qovery services through NGINX customization. Rate limiting helps protect services from abuse. While third-party solutions like Cloudflare are recommended for filtering traffic before it reaches workloads, this tutorial demonstrates cluster-level implementation.

<Warning>
  Changing NGINX snippets configuration is an advanced feature and can lead to misconfiguration. Please be careful when changing these settings as it might break the whole NGINX configuration.
</Warning>

## Prerequisites

* Application running on a Qovery managed cluster
* Basic understanding of NGINX rate limiting

## Understanding NGINX Rate Limiting

NGINX rate limiting protects your services by controlling the rate of requests. For detailed information, see [NGINX's official documentation on rate limiting](https://www.nginx.com/blog/rate-limiting-nginx/).

***

## Initial Setup

### Configure Test Service

We'll use `jmalloc/echo-server` Docker image for testing:

* Image: `jmalloc/echo-server`
* Port: 80

<Frame>
  <img src="https://mintcdn.com/qovery/cE_XSld-w75y9o8I/images/nginx-rate-limit/service-initial-setup-qovery-screen.png?fit=max&auto=format&n=cE_XSld-w75y9o8I&q=85&s=b1e6eb4cd97ea8a9740a20258eab5a72" alt="Service initial setup" width="1211" height="315" data-path="images/nginx-rate-limit/service-initial-setup-qovery-screen.png" />
</Frame>

### Configure NGINX Replicas

Rate limits apply per NGINX instance. For demonstration, set replicas to 1 (not recommended for production):

1. Go to **Cluster Settings** → **Advanced Settings**
2. Find `nginx.controller.replicas`
3. Set value to `1`

<Frame>
  <img src="https://mintcdn.com/qovery/cE_XSld-w75y9o8I/images/nginx-rate-limit/cluster-advanced-settings-replicas.png?fit=max&auto=format&n=cE_XSld-w75y9o8I&q=85&s=2e390c66c6dad6f209225e1a97a13bdf" alt="Set NGINX replicas" width="814" height="102" data-path="images/nginx-rate-limit/cluster-advanced-settings-replicas.png" />
</Frame>

<Info>
  Without rate limits: 100 req/sec × 500 requests = 100% success rate (all HTTP 200 responses)
</Info>

***

## Global Rate Limit Implementation

### Declare at Cluster Level

Configure `nginx.controller.http_snippet`:

```nginx theme={null}
limit_req_zone "$server_name" zone=global:10m rate=10r/s;
```

**Components:**

* `$server_name`: Rate limit key (can use `$http_x_forwarded_for` for IP-based limits)
* `zone=global:10m`: Zone name with 10MB memory allocation
* `rate=10r/s`: 10 requests per second threshold

<Frame>
  <img src="https://mintcdn.com/qovery/cE_XSld-w75y9o8I/images/nginx-rate-limit/cluster-advanced-settings-set-global-rate-in-http-snippet.png?fit=max&auto=format&n=cE_XSld-w75y9o8I&q=85&s=5bbc2130be93083a0fe3eb87c84d0591" alt="Set global rate in http snippet" width="1245" height="691" data-path="images/nginx-rate-limit/cluster-advanced-settings-set-global-rate-in-http-snippet.png" />
</Frame>

### Apply Rate Limit

Configure `nginx.controller.server_snippet`:

```nginx theme={null}
location / {
    limit_req zone=global;
}
```

<Frame>
  <img src="https://mintcdn.com/qovery/cE_XSld-w75y9o8I/images/nginx-rate-limit/cluster-advanced-settings-set-global-rate-in-server-snippet.png?fit=max&auto=format&n=cE_XSld-w75y9o8I&q=85&s=f63ad5ff1ef9df66963c17b11cbb1df5" alt="Apply global rate in server snippet" width="1272" height="663" data-path="images/nginx-rate-limit/cluster-advanced-settings-set-global-rate-in-server-snippet.png" />
</Frame>

### Deploy Changes

<Frame>
  <img src="https://mintcdn.com/qovery/cE_XSld-w75y9o8I/images/nginx-rate-limit/deploy-cluster-after-advanced-settings-changes.png?fit=max&auto=format&n=cE_XSld-w75y9o8I&q=85&s=b80df0c445eb1a2e2fef7274bf80cf4e" alt="Deploy cluster changes" width="1272" height="551" data-path="images/nginx-rate-limit/deploy-cluster-after-advanced-settings-changes.png" />
</Frame>

### Enhancement with Burst

Adding `burst=20 nodelay;` permits 20-request bursts without delays:

```nginx theme={null}
location / {
    limit_req zone=global burst=20 nodelay;
}
```

### Test Results

Load test: 100 req/sec for 500 total requests

**Result:**

* 452 HTTP 503 rejections
* 48 HTTP 200 acceptances (\~9.6 req/sec)

<Frame>
  <img src="https://mintcdn.com/qovery/cE_XSld-w75y9o8I/images/nginx-rate-limit/nginx-logs-rejecting-requests-with-global-rate.png?fit=max&auto=format&n=cE_XSld-w75y9o8I&q=85&s=56a43a64b0fe654deff6c1b37d877d2e" alt="Global rate limit logs" width="1268" height="627" data-path="images/nginx-rate-limit/nginx-logs-rejecting-requests-with-global-rate.png" />
</Frame>

***

## Service-Level Rate Limit

Configure rate limiting at the service level using advanced settings:

**Configuration Settings:**

* `network.ingress.nginx_limit_rps`: Rate in requests/second
* `network.ingress.nginx_limit_rpm`: Rate in requests/minute
* `network.ingress.nginx_limit_burst_multiplier`: Burst multiplier (default: 5)

### Example: 10 Requests/Minute with 2× Burst

<Frame>
  <img src="https://mintcdn.com/qovery/cE_XSld-w75y9o8I/images/nginx-rate-limit/service-advanced-settings-set-limit-rpm-and-burst.png?fit=max&auto=format&n=cE_XSld-w75y9o8I&q=85&s=0f33d3fb23d4f2016d3548fb0946a74b" alt="Service-level rate limit" width="1277" height="661" data-path="images/nginx-rate-limit/service-advanced-settings-set-limit-rpm-and-burst.png" />
</Frame>

<Info>
  When both RPM and RPS are set, both rate limits will be enforced simultaneously. Traffic will be restricted based on whichever limit is hit first.
</Info>

### Deploy Service

<Frame>
  <img src="https://mintcdn.com/qovery/cE_XSld-w75y9o8I/images/nginx-rate-limit/deploy-service.png?fit=max&auto=format&n=cE_XSld-w75y9o8I&q=85&s=be5d4cf7aeaa4fd0b990934609a69d1a" alt="Deploy service with rate limit" width="1275" height="643" data-path="images/nginx-rate-limit/deploy-service.png" />
</Frame>

### Test Results

* 479 HTTP 503 rejections
* 21 HTTP 200 acceptances

<Frame>
  <img src="https://mintcdn.com/qovery/cE_XSld-w75y9o8I/images/nginx-rate-limit/nginx-logs-rejecting-requests-with-service-rate.png?fit=max&auto=format&n=cE_XSld-w75y9o8I&q=85&s=544990c3a0858454508cc5fab775f2ce" alt="Service rate limit logs" width="1272" height="694" data-path="images/nginx-rate-limit/nginx-logs-rejecting-requests-with-service-rate.png" />
</Frame>

***

## Custom Header-Based Rate Limiting

Implement rate limiting based on custom HTTP headers.

### Cluster Configuration

Configure `nginx.controller.http_snippet`:

```nginx theme={null}
map $http_x_qovery_api_key $limit_key {
    default $http_x_qovery_api_key;
    "" "anonymous";
}

limit_req_zone $limit_key zone=qovery_api_limit:10m rate=5r/s;
```

<Frame>
  <img src="https://mintcdn.com/qovery/cE_XSld-w75y9o8I/images/nginx-rate-limit/cluster-advanced-settings-set-custom-header-rate-in-http-snippet.png?fit=max&auto=format&n=cE_XSld-w75y9o8I&q=85&s=ab64318a292f60527149a44e7a868461" alt="Custom header rate limit declaration" width="3410" height="1320" data-path="images/nginx-rate-limit/cluster-advanced-settings-set-custom-header-rate-in-http-snippet.png" />
</Frame>

**Details:** Maps HTTP header values to rate limit keys; missing headers default to "anonymous" designation.

### Service Configuration

Configure `network.ingress.nginx_controller_configuration_snippet`:

```nginx theme={null}
limit_req zone=qovery_api_limit burst=2 nodelay;
```

<Frame>
  <img src="https://mintcdn.com/qovery/cE_XSld-w75y9o8I/images/nginx-rate-limit/service-advanced-settings-set-customer-header-rate-limiter-in-configuration-snippet.png?fit=max&auto=format&n=cE_XSld-w75y9o8I&q=85&s=df9d3bca86489d721eecf52efc2622c9" alt="Apply custom header rate limit" width="3410" height="946" data-path="images/nginx-rate-limit/service-advanced-settings-set-customer-header-rate-limiter-in-configuration-snippet.png" />
</Frame>

**Burst Parameter Explanation:**

* `burst=2`: Permits 2 extra requests beyond rate limit during short bursts
* `nodelay`: Disables request queuing; excess requests rejected immediately

### Deploy and Test

<Frame>
  <img src="https://mintcdn.com/qovery/cE_XSld-w75y9o8I/images/nginx-rate-limit/deploy-service-with-custom-header-rate-limiter.png?fit=max&auto=format&n=cE_XSld-w75y9o8I&q=85&s=110c7bd8b417221e48b322d86132c0f6" alt="Deploy with custom header rate limit" width="3418" height="1302" data-path="images/nginx-rate-limit/deploy-service-with-custom-header-rate-limiter.png" />
</Frame>

**Test Command:**

```bash theme={null}
oha -q 100 -n 500 -H "X-QOVERY-API-KEY: benjamin"
```

**Result:**

* 473 HTTP 503 rejections
* 27 HTTP 200 acceptances (\~5.4 req/sec with burst)

<Frame>
  <img src="https://mintcdn.com/qovery/cE_XSld-w75y9o8I/images/nginx-rate-limit/nginx-logs-rejecting-requests-with-custom-header-in-service-logs.png?fit=max&auto=format&n=cE_XSld-w75y9o8I&q=85&s=017fda0f0e7608e3f59152d3c6e7ef3e" alt="Service logs with custom header" width="1273" height="703" data-path="images/nginx-rate-limit/nginx-logs-rejecting-requests-with-custom-header-in-service-logs.png" />
</Frame>

<Frame>
  <img src="https://mintcdn.com/qovery/cE_XSld-w75y9o8I/images/nginx-rate-limit/nginx-logs-rejecting-requests-with-custom-header.png?fit=max&auto=format&n=cE_XSld-w75y9o8I&q=85&s=33438702514a6263477627277dc1e1f7" alt="NGINX logs with custom header" width="1240" height="127" data-path="images/nginx-rate-limit/nginx-logs-rejecting-requests-with-custom-header.png" />
</Frame>

***

## Additional Configuration

### Custom HTTP Status Code

Configure `nginx.controller.limit_request_status_code`:

* Default: HTTP 503
* Alternative: HTTP 429 (commonly used for rate limiting)

<Frame>
  <img src="https://mintcdn.com/qovery/cE_XSld-w75y9o8I/images/nginx-rate-limit/cluster-advanced-settings-custom-limit-http-status.png?fit=max&auto=format&n=cE_XSld-w75y9o8I&q=85&s=d3d75216d4e6195bf98841b20b20b70c" alt="Custom HTTP status code" width="1276" height="577" data-path="images/nginx-rate-limit/cluster-advanced-settings-custom-limit-http-status.png" />
</Frame>

Test results show HTTP 429 responses:

<Frame>
  <img src="https://mintcdn.com/qovery/cE_XSld-w75y9o8I/images/nginx-rate-limit/cluster-advanced-settings-custom-limit-http-status-logs.png?fit=max&auto=format&n=cE_XSld-w75y9o8I&q=85&s=06dbd9e14c3deb96db7f8a3827374aff" alt="HTTP 429 response logs" width="3394" height="608" data-path="images/nginx-rate-limit/cluster-advanced-settings-custom-limit-http-status-logs.png" />
</Frame>

### Connection Limits

Configure `network.ingress.nginx_limit_connections` at service level to limit concurrent connections from single IP addresses.

***

## Summary

<AccordionGroup>
  <Accordion title="Global Rate Limit">
    Applied at cluster level, affects all services. Best for protecting the entire cluster from abuse.
  </Accordion>

  <Accordion title="Service-Level Rate Limit">
    Applied per service using `nginx_limit_rps` or `nginx_limit_rpm`. Best for protecting individual services.
  </Accordion>

  <Accordion title="Header-Based Rate Limit">
    Applied based on custom HTTP headers. Best for API key-based rate limiting and multi-tenant applications.
  </Accordion>

  <Accordion title="Connection Limits">
    Limits concurrent connections per IP. Best for preventing connection exhaustion attacks.
  </Accordion>
</AccordionGroup>

***

## Related Documentation

<CardGroup cols={2}>
  <Card title="Advanced Settings" icon="sliders" href="/configuration/service-advanced-settings">
    Configure service advanced settings
  </Card>

  <Card title="NGINX Configuration" icon="server" href="/configuration/integrations/api-gateway-nginx">
    Learn about NGINX ingress
  </Card>

  <Card title="Cluster Configuration" icon="layer-group" href="/configuration/clusters">
    Configure cluster settings
  </Card>

  <Card title="Security Best Practices" icon="shield" href="/getting-started/security-and-compliance/overview">
    Implement security measures
  </Card>
</CardGroup>
