nginx rate limit config: limit requests per IP address
Slow down abusive visitors with limit_req: a request rate per IP, a burst, and a 429 answer.
Rate limiting lets each visitor make only a set number of requests per second. It protects login pages and slow routes from bots and floods. Choose your rate and burst below and add the result to your server.
Create your config file
Type your own values. The file below updates as you type, and nothing you type leaves your browser.
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=strict:10m rate=10r/m;
limit_req_status 429;
server {
listen 80;
server_name example.com;
location / {
limit_req zone=general burst=20 nodelay;
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location = /login {
limit_req zone=strict burst=5 nodelay;
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}How this setup works
Step-by-step setup
- Save the config above as /etc/nginx/conf.d/ratelimit.conf, or merge the lines into your own server block.
- Test the config: sudo nginx -t.
- Apply it: sudo systemctl reload nginx.
- Try it: for i in $(seq 1 60); do curl -s -o /dev/null -w "%{http_code}\n" http://example.com/; done. After the burst you should see 429.
- Watch the error log for lines that say limiting requests.
What the important lines mean
- limit_req_zone makes a shared memory area that counts requests per IP address. It must be at the top level, outside any server block.
- rate=10r/s is the average number of requests allowed per second.
- burst=20 lets a visitor go over the rate briefly. nodelay serves the burst at once instead of spacing it out.
- limit_req_status 429 answers "Too Many Requests" instead of the default 503.
Common errors and how to fix them
- unknown "binary_remote_addr" variable or limit_req_zone not allowed here
- limit_req_zone must be in the http level (a file in conf.d is fine), not inside server or location.
- Everyone is limited behind a proxy or CDN
- nginx sees only the proxy's address. Use the real client address from the proxy header with the realip module, then key the zone on $binary_remote_addr.
- Real visitors get 429
- The rate is too low for pages that load many files. Raise the burst, or put the limit only on the paths that need it, such as login.
- zero size shared memory zone
- The zone name in limit_req must match one defined by limit_req_zone. Check for a typo.
Frequently asked questions
- What does burst do?
- It lets a visitor send a short spike above the rate. Requests beyond the burst are refused. Without it every extra request is refused at once.
- Does rate limiting stop DDoS attacks?
- It helps against simple floods from a few addresses. It does not stop large attacks from many addresses. Use a CDN or protection service for those.
- Why 429 and not 503?
- 429 Too Many Requests tells clients that they are being limited. The nginx default is 503, which looks like a server failure.
Free tools that help
- Common ports listSearch a list of common TCP and UDP port numbers: SSH 22, HTTP 80, HTTPS 443, MySQL 3306, PostgreSQL 5432 and more, with dev server ports.
Fix a problem with this setup
- nginx 502 Bad GatewayFix nginx 502 Bad Gateway with Node, PHP-FPM or any proxy: read the error log, check the app is running and the port or socket matches.
- CORS error on localhostFix "blocked by CORS policy" when your frontend on localhost calls an API: add the right headers on the server, or use a dev proxy.
More server configs
- php.ini settingsSet the PHP values most sites need: upload size, memory, run time, errors, time zone and OPcache.
- MySQL my.cnf settingsA small MySQL or MariaDB override file for connections, memory, character set and the slow query log.
- nginx reverse proxy configSend visitors from port 80 to an app running on port 3000.
- nginx PHP-FPM configRun a PHP site (WordPress, Laravel or plain PHP) on nginx with PHP-FPM.
- Caddyfile reverse proxySend visitors to an app on port 3000 and get HTTPS certificates automatically.
- crontab examplesRun a script every minute, hour, day or week.