Nginx and Varnish

superfedya's picture


Can somebody show me an example of config Nginx+Varnish? It is complicated to make them work together?

I use Perusio's Nginx config and I must switch from Boost to Varnish...



Doesn't nginx have a cache as well?

BTMash's picture

I thought Perusio's config took advantage of nginx's own cache for caching pages?

I took what was a setup described by Lullabot and set mine up similar to that. Mind you, this is *not* for Varnish 3.x and I don't know what you would need to change for that. And this is also before I found out about all the stuff NGinx can do. In any case, here is my setup (I have nginx/apache set up to listen on another port):

# This is a basic VCL configuration file for varnish.  See the vcl(7)
# man page for details on VCL syntax and semantics.
# Default backend definition.  Set this to point to your content
# server.

backend default {
    .host = "";
    .port = "10000";
    .connect_timeout = 600s;
    .first_byte_timeout = 600s;
    .between_bytes_timeout = 600s;

sub vcl_recv {

  remove req.http.X-Forwarded-For;
  set req.http.X-Forwarded-For = client.ip;

  if (
    req.request != "GET" &&
    req.request != "HEAD" &&
    req.request != "PUT" &&
    req.request != "POST" &&
    req.request != "TRACE" &&
    req.request != "OPTIONS" &&
    req.request != "DELETE") {
      /* Non-RFC2616 or CONNECT which is weird. */
      return (pipe);

  if (req.request != "GET" && req.request != "HEAD") {
    /* We only deal with GET and HEAD by default */
    return (pass);

  // Remove has_js and Google Analytics __* cookies.
  set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(__[a-z]+|has_js)=[^;]*", "");
  // Remove a ";" prefix, if present.
  set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", "");
  // Remove empty cookies.
  if (req.http.Cookie ~ "^\s*$") {
    unset req.http.Cookie;

// Skip the Varnish cache for install, update, and cron
  if (req.url ~ "install\.php|update\.php|cron\.php|admin\.php|batch\.php") {
    return (pass);

# Do not cache these paths.
if (req.url ~ "^/status\.php$" ||
    req.url ~ "^/update\.php$" ||
    req.url ~ "^/ooyala/ping$" ||
    req.url ~ "^/admin/build/features" ||
    req.url ~ "^/info/.*$" ||
    req.url ~ "^/batch/.*$" ||
    req.url ~ "^/batch/.*$" ||
    req.url ~ "^/flag/.*$" ||
    req.url ~ "^.*/ajax/.*$" ||
    req.url ~ "^.*/ahah/.*$") {
     return (pass);

if (req.http.Authorization || req.http.Cookie) {
    /* Not cacheable by default */
    return (pass);

// Normalize the Accept-Encoding header
  // as per:
  if (req.http.Accept-Encoding) {
    if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
      # No point in compressing these
      remove req.http.Accept-Encoding;
    elseif (req.http.Accept-Encoding ~ "gzip") {
      set req.http.Accept-Encoding = "gzip";
    else {
      # Unknown or deflate algorithm
      remove req.http.Accept-Encoding;

# Pipe these paths directly to Apache for streaming.
  if (req.url ~ "^/admin/content/backup_migrate/export") {
    return (pipe);

# Do not allow outside access to cron.php or install.php.
  if (req.url ~ "^/(cron|install)\.php$") {
    # Have Varnish throw the error directly.
    error 404 "Page not found.";
    # Use a custom error page that you've defined in Drupal at the path "404".
    # set req.url = "/404";

# Handle compression correctly. Different browsers send different
  # "Accept-Encoding" headers, even though they mostly all support the same
  # compression mechanisms. By consolidating these compression headers into
  # a consistent format, we can reduce the size of the cache and get more hits.=
  # @see: http://
  if (req.http.Accept-Encoding) {
    if (req.http.Accept-Encoding ~ "gzip") {
      # If the browser supports it, we'll use gzip.
      set req.http.Accept-Encoding = "gzip";
    else if (req.http.Accept-Encoding ~ "deflate") {
      # Next, try deflate if it is supported.
      set req.http.Accept-Encoding = "deflate";
    else {
      # Unknown algorithm. Remove it and send unencoded.
      unset req.http.Accept-Encoding;

# Always cache the following file types for all users.
  if (req.url ~ "(?i)\.(png|gif|jpeg|jpg|ico|swf|css|js|html|htm)(\?[a-z0-9]+)?$") {
    unset req.http.Cookie;

# Remove all cookies that Drupal doesn't need to know about. ANY remaining
  # cookie will cause the request to pass-through to Apache. For the most part
  # we always set the NO_CACHE cookie after any POST request, disabling the
  # Varnish cache temporarily. The session cookie allows all authenticated users
  # to pass through as long as they're logged in.
  if (req.http.Cookie) {
    set req.http.Cookie = ";" req.http.Cookie;
    set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(SESS[a-z0-9]+|NO_CACHE)=", "; \1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
    set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");

    if (req.http.Cookie == "") {
      # If there are no remaining cookies, remove the cookie header. If there
      # aren't any cookie headers, Varnish's default behavior will be to cache
      # the page.
      unset req.http.Cookie;
    else {
      # If there is any cookies left (a session or NO_CACHE cookie), do not
      # cache the page. Pass it on to Apache directly.
      return (pass);

  if (req.url ~ "node\?page=[0-9]+$") {
         set req.url = regsub(req.url, "node(\?page=[0-9]+$)", "\1");
         return (lookup);

// Let's have a little grace
  set req.grace = 30s;

  return (lookup);


sub vcl_hash {
  if (req.http.Cookie) {
    set req.hash += req.http.Cookie;

# Code determining what to do when serving items from the Apache servers.
sub vcl_fetch {
  # Don't allow static files to set cookies.
  if (req.url ~ "(?i)\.(png|gif|jpeg|jpg|ico|swf|css|js|html|htm)(\?[a-z0-9]+)?$") {
    # beresp == Back-end response from the web server.
    unset beresp.http.set-cookie;

# Allow items to be stale if needed.
  set beresp.grace = 6h;

sub vcl_error {
   // Let's deliver a friendlier error page.
   // You can customize this as you wish.
   set obj.http.Content-Type = "text/html; charset=utf-8";
   synthetic {"
   <?xml version="1.0" encoding="utf-8"?>
   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
       <title>"} obj.status " " obj.response {"</title>
       <style type="text/css">
       #page {width: 400px; padding: 10px; margin: 20px auto; border: 1px solid black; background-color: #FFF;}
       p {margin-left:20px;}
       body {background-color: #DDD; margin: auto;}
     <div id="page">
       <h1>Page Could Not Be Loaded</h1>
       <p>We're very sorry, but the page could not be loaded properly. This should be fixed very soon, and we apologize for any inconvenience.</p>
       <hr />
       <h4>Debug Info:</h4>
       <pre>Status: "} obj.status {"Response: "} obj.response {"XID: "} req.xid {"</pre>

Let me know if that helps.

Why Varnish?

perusio's picture

Why make your setup overcomplicated? Does Varnish add anything to using the Nginx cache? The config comes with a microcaching setup enabled. You can tune that setting as much as you want.

If you want to go further than use Redis. But using the Nginx cache is rather simple. Why complicate when there's simplicity to be used?

are you able to prevent nginx

seandunaway's picture

are you able to prevent nginx from serving from cache when cookie is present or for authenticated users?

that's been my only concern with swapping out varnish for nginx - i thought nginx was all or nothing with its cache.

Yes. Nginx is very

brianmercer's picture

Yes. Nginx is very configurable: either through its own configuration syntax or for advanced users through embedded languages such as lua or perl.

Nginx microcaching will

superfedya's picture

Nginx microcaching will reduse the requests to database like Boost or Varnish?
Nginx cache+boost it's only for drupal. Varnish I can use with drupal and phpbb3.

ps. where is nginx keeps its cache? I found a nginx cache directory and its empty (with many empty subfolders)?

perusio's picture

the config sets /var/cache/nginx as the cache directory. The Nginx cache works with any type of FCGI or proxy upstream. So phpbb3 is covered.

Nginx cache can reduse

superfedya's picture

Nginx cache can reduse requests to mysql db?


I verified my

superfedya's picture

I verified my /var/cache/nginx and there is many folders and each one is empty.
upd: in /var/cache/nginx/microcache I found 258 folders and 37 files...
Folder permission is 700.

Can I use cache warmer with and

That has nothing to do with

perusio's picture

an external cache. The redis module implements a drupal cache backend, i.e., it's a drupal cache.

The permissions are correct. The reason why it's empty is because you're not using it.

Yes it doesn't touch the DB, it works at the HTTP server level.

Why I not using microcache?

superfedya's picture

Why Im not using microcache? In nginx config it specified to use microcache for annon and auth users.


perusio's picture

you're using the Boost config and not the "regular" drupal config. That one has microcaching activated by default. Include the drupal.conf file instead of drupal_boost.conf (on D7). You can choose if you want to use it with anon and/or authenticated users.

So, nginx microcaching

superfedya's picture

So, nginx microcaching doesn't work with Boost module? :(
Can I activate a microcaching for boost config? (D6).

In my conf there is the line:
include sites-available/microcache_fcgi_auth.conf;

I verified it again and now I have 850 files and 4113 folders in /var/cache/nginx/microcache.


Sure thing

perusio's picture

it works. It's just that you either use one or the other usually. The exception is authenticated users. You get a very small TTL for the cache that helps and that doesn't interfere with Boost. My suggestion is for you to try microcaching without Boost and see how it goes.

You can use this script to inspect a cache file and see to which URI and user it corresponds to. Note that for authenticated users each user gets is own cache.

I'm following up here with

BTMash's picture

I'm following up here with some small benchmarks that I did. I disabled varnish and I used @perusio's bleeding edge config on my site Previously with varnish, my site could handle 3k - 3.5k requests per second. With just nginx and microcaching, it can handle 5k - 5.5k requests per second. Its a pretty significant boost. And there are fewer parts to worry about :)

Sorry to resurrect and old

sb56637's picture

Sorry to resurrect and old thread...

So what would you guys recommend here for a site with about half and half registered and authenticated users (but authenticated users posting a lot, which creates more load)? Is regular Nginx microcaching for anonymous users more effective than Boost?

Nginx is more effective than

chilic's picture

Nginx is more effective than Boost. But i recomend you Varnish or Squid cache with expires module.

Also see Authcache module.

memcache for authenticated users

jamonation's picture

This doesn't have much to do with nginx per se, but you'll benefit from using memcache for your authenticated users.

nginx with a proxy_cache that resides on a tmpfs* in RAM will make a difference for microcaching authenticated users. Put Boost's cache directory on a tmpfs* too and then both caches live in memory, obviating the need for varnish mostly (unless you really like VCL logic :p )

You can easily run like this:

  • nGinx + proxy_cache (use a tmpfs here)
  • Drupal+Boost on php-fpm (put Boost's files on a tmpfs)
  • Memcache

With this setup you don't need a varnish server since proxy_cache and Boost function the same way that varnish would be, and you get the benefit of caching a lot of data for authenticated users too. which I think ultimately is something that you're after.

*Keep in mind that with tmpfs, any writes to it can use swap depending on what the kernel decides, so you will want to control your swappiness if you are using linux.

Interesting suggestions,

sb56637's picture

Interesting suggestions, thanks!

I only have 1GB of RAM on the server, and I'm using a lot of it for the mySQL server. So my main interest isn't raw speed, but taking the load off the database. So it looks like Nginx + proxy_cache together with Drupal + Boost, all on the disk should take some load off the database? It should be reasonably fast still, because it's an SSD disk.

I might re-implement Redis or Memcache later on. I used to use it, but I didn't see much reduction in DB requests and I found it to be a bit buggy.

Thanks for the suggestions!