# Nginx FastCGI response buffer sizes By default when Nginx starts receiving a response from a FastCGI backend (such as [PHP-FPM](https://php.net/manual/en/install.fpm.php)) it will buffer the response in memory before delivering it to the client. Any response larger than the set buffer size is saved to a temporary file on disk. This process is outlined at the Nginx [ngx_http_fastcgi_module page](https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_buffering) manual page. - [Introduction](#introduction) - [Determine actual FastCGI response sizes](#determine-actual-fastcgi-response-sizes) - [Setting the buffer size](#setting-the-buffer-size) - [Verifying results](#verifying-results) ## Introduction Since disk is *slow* and memory is **fast** the aim is to get as many FastCGI responses passing only through memory. On the flip side we don't want to set an excessively large buffer as they are created and sized on a *per request basis* - it's **not** shared. The related Nginx options are: - [`fastcgi_buffering`](https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_buffering) first appeared in Nginx `1.5.6` (`1.6.0` stable) and can be used to turn buffering completely on/off. It's **on** by default. - [`fastcgi_buffer_size`](https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_buffer_size) is a special buffer space used to hold the _first chunk_ of the FastCGI response, which is going to be HTTP response headers. You _shouldn't_ need to adjust this from the default - even if Nginx defaults to the smallest page size of `4k` (your platform will determine the default of `4k/8k` buffers) - it should be able to fit typical HTTP response headers. The one possible exception - frameworks that push large amounts of cookie data via the `Set-Cookie` HTTP header during user verification/login phases - blowing out the buffer and resulting in HTTP `500` errors. In these instances you will need to *increase* this buffer to `8k/16k/32k` to fully accommodate the largest upstream HTTP header being sent. - [`fastcgi_buffers`](https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_buffers) controls the _number and size_ of buffer segments used for the payload of each FastCGI response. Most, if not all of our adjustments will be around this setting for the remainder of this guide. ## Determine actual FastCGI response sizes By grepping our Nginx access logs we can determine both maximum and average response sizes. The basis of this `awk` recipe was [lifted from here](https://easyengine.io/tutorials/nginx/tweaking-fastcgi-buffers/): ```sh $ awk '($9 ~ /200/) { i++;sum+=$10;max=$10>max?$10:max; } END { printf("Maximum: %d\nAverage: %d\n",max,i?sum/i:0); }' access.log # Maximum: 76716 # Average: 10358 ``` > [!NOTE] > These recipes will report on **all** access requests returning an HTTP 200 code, you might want to split out _just_ FastCGI requests into a separate access log for reporting, like so (PHP-FPM here): ```nginx location ~ "\.php$" { fastcgi_index index.php; if (!-f $realpath_root$fastcgi_script_name) { return 404; } include /etc/nginx/fastcgi_params; fastcgi_pass unix:/run/php5/php-fpm.sock; # output just FastCGI requests to it's own Nginx log file access_log /var/log/nginx/phpfpm-only-access.log; } ``` With these values in hand we're now much better equipped to set `fastcgi_buffers`. ## Setting the buffer size As noted earlier, the [`fastcgi_buffers`](https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_buffers) setting takes two values, )_buffer segment count_ and _memory size_, by default this will be: ``` fastcgi_buffers 8 4k|8k; ``` So a total of 8 buffer segments at either `4k/8k`, which is determined by the platform memory page size. For Debian/Ubuntu Linux that turns out to be `4096` bytes (`4KB`) - so a default total buffer size of `8 * 4K = 32KB`. Based on the maximum/average response sizes [determined above](#determine-actual-fastcgi-response-sizes) we can now raise/lower these values to suit. I typically keep buffer size at the default (memory page size) and adjust _only the buffer segment count_ to a value that keeps the bulk of responses handled fully in buffer RAM. The default memory page size (in bytes) for an operating system can be determined by the following command: ```sh $ getconf PAGESIZE ``` If your response size average tips on the higher side you might want to alternatively _lower_ the buffer segment count and raise the memory size in page size multiples (`8k/16k/32k`). ## Verifying results We can see how often FastCGI responses are saved out to temporary disk by grepping Nginx error log(s): ```sh $ cat error.log | grep --extended-regexp "\[warn\].+buffered" # will return lines like: YYYY/MM/DD HH:MM:SS [warn] 1234#0: *123456 an upstream response is buffered to a temporary file... ``` > [!TIP] > Remember its not necessarily bad to have *some* larger responses buffered to disk, but aim for a balance where ideally a smaller portion of your larger responses are handled in this way. The alternative of ramping up `fastcgi_buffers` to excessively large number and/or size values to fit all FastCGI responses purely in RAM is something I would _strongly_ recommend against. Unless your Nginx server is receiving only a few concurrent requests at any one moment - you risk exhausting your available system memory.