Never Ending Security

It starts all here

How To Use Nginx FastCGI Caching

How to: Nginx FastCGI Caching

Nginx includes a FastCGI module directives for caching dynamic content that are served from the PHP backend. Many administrators reach for Varnish, often before it’s really needed. While Varnish is a pure web cache with more advanced cache-specific features than Nginx, Nginx may still be a perfect match for you. If your traffic warrants adding a layer of infrastructure for caching, but not the overhead of introducing new technologies that need to be learned and maintained, Nginx might be a better fit. Setting this up removes the need for additional page caching solutions like reverse proxies or application specific plugins.

Enable FastCGI

Edit the Virtual Host configuration file for which caching has to be enabled.

nano /etc/nginx/sites-enabled/vhost

Add the following lines to the top of the file outside the server { } directive:

fastcgi_cache_path /etc/nginx/cache levels=1:2 keys_zone=MYAPP:100m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
  • The “fastcgi_cache_path” directive specifies the location of the cache (/etc/nginx/cache), its size (100m), memory zone name (MYAPP), the subdirectory levels, and the inactive` timer. The location can be anywhere on the hard disk; however, the size must be less than your RAM + Swap space or you’ll receive an error that reads “Cannot allocate memory”. if a cache isn’t accessed for a particular amount of time specified by the “inactive” option (60 minutes here), then Nginx removes it.
  • The “fastcgi_cache_key” directive specifies how the the cache filenames will be hashed. Nginx encrypts an accessed file with MD5 based on this directive.

Next, move the location directive that passes PHP requests to php5-fpm. Inside “location ~ .php$ { }” add the following lines.

fastcgi_cache MYAPP;
fastcgi_cache_valid 200 60m;
  • The “fastcgicache” directive references to the memory zone name specified in thefastcgi_cache_path”directive and stores the cache in this area. By default Nginx stores the cached objects for a duration specified by any of these headers: X-Accel-Expires/Expires/Cache-Control.
  • The “fastcgicachevalid” directive is used to specify the default cache lifetime if these headers are missing. In the above statement only responses with a status code of 200 are cached.

Do a configuration test

service nginx configtest

Reload Nginx if everything is OK

service nginx reload

The complete vhost file will look like this:

fastcgi_cache_path /etc/nginx/cache levels=1:2 keys_zone=MYAPP:100m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";

server {
    listen   80;

    root /usr/share/nginx/html;
    index index.php index.html index.htm;


    location / {
        try_files $uri $uri/ /index.html;

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_cache MYAPP;
        fastcgi_cache_valid 200 60m;

Test FastCGI Caching

Create a PHP file which outputs a UNIX timestamp.



echo time();

Request this file multiple times using curl or your web browser. curl http://localhost/time.php;echo
1382986152 curl http://localhost/time.php;echo
1382986152 curl http://localhost/time.php;echo

If caching works properly, you should see the same timestamp on all requests as the response is cached.

Do a recursive listing of the cache location to find the cache of this request. ls -lR /etc/nginx/cache/
total 0
drwx------ 3 www-data www-data 60 Oct 28 18:53 e

total 0
drwx------ 2 www-data www-data 60 Oct 28 18:53 18

total 4
-rw------- 1 www-data www-data 117 Oct 28 18:53 b777c8adab3ec92cd43756226caf618e

We can also make Nginx add a “X-Cache” header to the response, indicating if the cache was missed or hit.

Add the following above the server { } directive:

add_header X-Cache $upstream_cache_status;

Reload the Nginx service and do a verbose request with curl to see the new header. curl -v http://localhost/time.php
* About to connect() to localhost port 80 (#0)
*   Trying
* connected
* Connected to localhost ( port 80 (#0)
> GET /time.php HTTP/1.1
> User-Agent: curl/7.26.0
> Host: localhost
> Accept: */*
* HTTP 1.1 or later with persistent connection, pipelining supported
< HTTP/1.1 200 OK
< Server: nginx
< Date: Tue, 10 Oct 2014 14:03:54 GMT
< Content-Type: text/html
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Cache: HIT
* Connection #0 to host localhost left intact
1383045828* Closing connection #0

Setting Cache Exceptions

Some dynamic content such as authentication required pages shouldn’t be cached. Such content can be excluded from being cached based on server variables like “requesturi,” “requestmethod,” and “http_cookie.”

#Cache everything by default
set $no_cache 0;

#Don't cache POST requests
if ($request_method = POST)
    set $no_cache 1;

#Don't cache if the URL contains a query string
if ($query_string != "")
    set $no_cache 1;

#Don't cache the following URLs
if ($request_uri ~* "/(administrator/|login.php)")
    set $no_cache 1;

#Don't cache if there is a cookie called PHPSESSID
if ($http_cookie = "PHPSESSID")
    set $no_cache 1;

To apply the “$no_cache” variable to the appropriate directives, place the following lines inside location ~ .php$ { }

fastcgi_cache_bypass $no_cache;
fastcgi_no_cache $no_cache;

The “fasctcgicachebypass” directive ignores existing cache for requests related to the conditions set by us previously. The “fastcginocache” directive doesn’t cache the request at all if the specified conditions are met.

Purging the Cache

The naming convention of the cache is based on the variables set in the “fastcgicachekey” directive.

fastcgi_cache_key "$scheme$request_method$host$request_uri";

According to these variables, when we requested “http://localhost/time.php” the following would’ve been the actual values:

fastcgi_cache_key "httpGETlocalhost/time.php";

Passing this string through MD5 hashing would output the following string:


This will form the filename of the cache as for the subdirectories we entered “levels=1:2.” Therefore, the first level of the directory will be named with 1 character from the last of this MD5 string which is e; the second level will have the last 2 characters after the first level i.e. 18. Hence, the entire directory structure of this cache is as follows:


Based on this cache naming format you can develop a purging script in your favourite language.

Leave a Reply

Please log in using one of these methods to post your comment: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s