Nginx Proxy Snippets
By default, all web sites are configured to pass through our Nginx Proxy servers un-altered.
However, if a site comes under attack or is overloaded, we can add a custom nginx proxy configuration to try to cache or block some of the requests.
Big picture
The first step is to determine which web proxy is being used by the site. You can dig the domain name or look it up in the DNS section of the control panel.
Next, login to that web proxy and edit the /etc/nginx/sites-enabled/siteNNN.conf file directly.
Be sure to test with mf-nginx-t with each change.
When you are satisfied that it is working, edit the control panel web configuration record, choose “Custom cache settings” as the Cache type, and copy and paste the entire contents into the “Cache Settings” field.
Snippets
Basic caching
The easiest step is to turn on basic caching. Change the location stanza from:
location / {
proxy_pass https://weborigin005_tls;
}
To:
location / {
proxy_pass https://weborigin005_tls;
proxy_cache my_cache;
proxy_cache_valid 15m;
expires 15m;
include snippets/performance-cache.conf;
}
Be sure to adjust the weborigin!
Using a CMS like Drupal or WordPress
Also, depending on whether it’s WordPress, Drupal (or something else), you will need to add a stanza to not cache the login or admin pages:
location ~* ^/(wp-admin|wp-cron.php|admin) {
proxy_pass https://weborigin005_tls;
}
A more complicated site may need additional stanzas to account for other pages with dynamic content.
And also, you will want to ensure logged in users do not have the content cached with:
proxy_no_cache $wordpress_logged_in;
proxy_cache_bypass $wordpress_logged_in;
You can substitute $drupal_logged_in for $wordpress_logged_in. These
settings will be set based on whether the web client is sending cookies
matching the ones set by WordPress and Drupal (7 and 8+) to indicate a user is
logged in.
Here is the subtle difference between the two:
proxy_no_cacheensures that if the client sends a logged in cookie, the page requested will not be cached. That ensures that regular users avoid seeing logged in page details (like admin menus).proxy_cache_bypassensures that the if the page being requested has been cached, the user will get a fresh copy, not the cached copy.
Note: Do not use these variables to mitigate a DDOS attack! They are easily spoofed.
No really, cache almost everything
If the site is still getting pounded, you can try the nuclear option, replacing
include snippets/performance-cache.conf; with include snippets/ddos-cache.conf;
The ddos-cache configuration converts POSTs to GETs and more aggressively caches all content.
Enabling exceptions.
At the top of the configuration, you can include something like this:
map $http_host $mysite_skip_cache {
default 0;
supersecret.domain.org 1;
}
And then, in the server stanza (not inside any location block), add:
proxy_cache_bypass $mysite_skip_cache;
proxy_no_cache $mysite_skip_cache;
The map above is based on the $http_host but it could be anything.
Rate limiting
By location
We have built in zones to rate limit by location, so it is very easy.
You can simply add the following to any location stazna:
limit_req zone=four_per_min burst=2 nodelay;
#limit_req zone=ten_per_min burst=2 nodelay;
#limit_req zone=thirty_per_min burst=2 nodelay;
#limit_req zone=sixty_per_min burst=2 nodelay;
These will limit a single IP to the number of requests per minute specified by the name.
If those limits don’t work for you, you can design your own.
Add the following to the top of the configuration:
# Describe your limit here so everyone knows what you are tryign to do.
limit_req_zone $binary_remote_addr zone=mysite_limit_by_ip:10m rate=2r/m;
Note: this configuration is named `mysite_limit_by_ip" but you can name it whatever you want. Try to use a name unique to the site to avoid conflicts.
That example bases the rate limit on the IP address and limits it to 2 requests per minute.
Next, add the following to any location stazna (note the repetition of the name):
limit_req zone=mysite_limit_by_ip burst=2 nodelay;
By query parameter
Sometimes you want to rate limit by a query parameter instead. Query parameters are not consulted when the location is determined.
Here’s an example:
# Set limit on any queries with the name "p" (e.g. `?p=123`). If you had `$arg_q` it
# could match on the query `?q=foo`
map $arg_p $mysite_is_page_query {
# If the query value for "p" contains "%2Fpage%2F" then record the IP
"~%2Fpage%2F" $binary_remote_addr;
# Otherwise, record nothing
default "";
}
limit_req_zone $mysite_is_page_query zone=mysite_page_query_limit:10m rate=4r/m;
Then, somehwere in the http section add:
limit_req zone=mysite_page_query_limit burst=5 nodelay;
Proxy buffers
If you get this error in the proxy log:
upstream sent too big header while reading response header from upstream, client
It means the origin is sending huge headers. If you want to see what they are sending, you can run:
curl -I https://example.org/
By default we only allow 16k of headers To change that add, the following to the http block:
proxy_buffers 8 32k;
proxy_buffer_size 48k;
Read more here.