Nginx + Varnish + Symfony2 Because caching is important!

This site is using Varnish now to selectively cache elements using Edge Side includes. Quite easy actually to do in symfony. You just have to enable it.

Varnish Configuration:

backend default {
    .host = "127.0.0.1";
    .port = "8080";
}
acl purge {
        "localhost";
}
sub vcl_recv {
    if (req.request == "PURGE") {
        if (!client.ip ~ purge) {
            error 405 "Not allowed.";
        }
        return(lookup);
    }

    set req.http.Surrogate-Capability = "abc=ESI/1.0";
    unset req.http.Accept-Encoding;

    ## Check if we are having cookies which we might need to strip
    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, ";(PHPSESSID)=", "; \1=");
        set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");

        if (req.url ~ "\.(png|gif|jpg|css|js)$") {
            remove req.http.Cookie;
        }

        if (req.http.Cookie == "") {
            remove req.http.Cookie;
        }

        if (req.http.x-is-anonymous) {
            remove req.http.Cookie;
        }
    }
}
sub vcl_hit {
    if(req.request == "PURGE") {
        set obj.ttl = 0s;
        error 200 "Purged.";
    }
}
sub vcl_miss {
    if(req.request == "PURGE") {
        error 404 "Not in cache.";
    }
}
sub vcl_fetch {
    if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
        unset beresp.http.Surrogate-Control;
        esi;
    }
    if (beresp.http.x-is-anonymous) {
        remove req.http.Cookie;
        unset beresp.http.set-cookie;
    }

    if (beresp.http.cache-control ~ "no-cache") {
        set beresp.ttl = 0s;
    }
    if (req.url ~ "\.(png|gif|jpg|css|js)$") {
        unset beresp.http.set-cookie;
        set beresp.ttl = 1h;
    }
}    

NginX config:

map $server_port $ssl_enabled {
    default off;
    443 on;
}
server {
    listen 8080;

    root /var/www/$host/current/web;
    index app.php;

    location / {
        try_files /maintenance.html $uri $uri/ /app.php;
    }

    location ~* \.(ico|css|js|gif|jpe?g|png)(\?[0-9]+)?$ {
        try_files $uri /app.php;
        expires max;
        break;
    }

    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_param SCRIPT_FILENAME /Users/mbeerta/Sites/$host/web$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_script_name;
        fastcgi_param HTTPS $ssl_enabled;
        include fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }
}

The only caveat is that the 2.1 version of varnish cannot handle gzip compression correctly. This can be solved though by adding a small nginx server in front of it which handles the compression for you:

upstream cache {
    server localhost:6081;        # Varnish
    server localhost:8080 backup; # Symfony app (in case varnish fails)
}
server {
    listen 80;

    location / {
        proxy_pass http://cache;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        gzip on;
    }

}

Super lazy nginx setup for symfony applications

I love nginx, did I say that?

Here is a super simple nginx server config which allows you to use any folder within your Websites directory to act as its own host. Need to quickly quickstart a new app? No problem, go to ~/Sites in a terminal and fetch the standard repository:

$ git clone git://github.com/symfony/symfony-standard.git symfony.dev.local
$ bin/vendors install

Thats it, never have to touch any configuration again. As a small bonus, you can also install dnsmasq and rewrite do 127.0.0.1 for even more laziness.*.dev.local

map $server_port $ssl_enabled {
    default off;
    443 on;
}

server {
    listen      80 default_server;

    #If you want to use SSL
    #listen     443 default_server ssl;
    #ssl_certificate     ssl/server.in.crt;
    #ssl_certificate_key ssl/server.in.key;

    root        /Users/mazen/Sites/$host/web;
    index       app_dev.php;

    location / {
        try_files   /maintenance.html $uri $uri/ /app_dev.php;
    }

    location ~ \.php {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_param SCRIPT_FILENAME /Users/mazen/Sites/$host/web$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_script_name;
        fastcgi_param HTTPS $ssl_enabled;
        include fastcgi_params;
    }

}

This is pretty much a standard nginx configuration. The tricky bit is the HTTPS part and that block containing . Symfony wants to have an HTTPS header which most web servers set but nginx does not. Using that map it figures out which port the server is running on and sets the variable value respectively.map $server_port $ssl_enabled