Squid is a fundamental part of our infrastructure at Fabulously40. It helps us lower response times quite considerably. The problem with Squid is that it is quite “dense” when it comes to configuration flexibility. Unless your willing to do a bit of C hacking on it, it does not have much configuration flexibility. This can be overcome by using supporting software to help squid out.

Note: Our configuration would be quite simplified if we used Varnish but it lacks some key features that make Squid a better candidate.

1. Varnish can’t stream cache-misses, it can only buffer. This adds latency to cache-miss requests.
2. Varnish is unable to avoid caching objects based on content-length size.
3. Varnish has an issue with connect_timeout and Solaris socket handling.

Until Varnish can handle the three things listed, Squid remains the best choice at the cost of configuration complexity.

Optimize Cache Hits by Normalizing Headers

“Accept-Encoding: gzip” and “Accept-Encoding: gzip/deflate” will be cached separately unless you normalize client headers. Squid has no configuration option to normalize headers like Varnish. However, you can use Nginx to normalize headers before passing off the request to Squid.

Here is the setup: Client -> Nginx -> Squid -> Backend Services

The NGINX Configuration


location / {
    proxy_set_header  X-Real-IP  $remote_addr;
    proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_hide_header Pragma;

    # Remove client cache-control header 
    # to avoid fetching from backend if page is in cache
    proxy_set_header Cache-Control "";

    # Normalize static assets for squid
    if ($request_uri ~* "\.(jpeg|jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|ico|swf|mp4|flv|mov|dmg|mkv)") {
        set $normal_encoding "";
        proxy_pass http://squids;
        break;
    }
    
    # Normalize gzip encoding
    if ($http_accept_encoding ~* gzip) {
        set $normal_encoding "gzip";
        proxy_pass http://squids;
        break;
    }
    
    # Normalize deflate encoding
    if ($http_accept_encoding ~* deflate) {
        set $normal_encoding "deflate";
        proxy_pass http://squids;
        break;
    }
    # Define the normalize header
    proxy_set_header Accept-Encoding $normal_encoding;

    # default...
    proxy_pass http://squids;
    break;
}

So by the time Squid receives the request, the accept-encoding header is normalized, for efficient cache storage.

Avoid Caching Error Pages with Squid 2.7

Squid 2.7 has better support for reverse caching and HTTP 1.1 than the Squid 3.x branch. However, it missing one important ACL that Squid 3.x has but 2.7 does not; http_status. Squid 2.7 is unable to be configured to avoid caching pages based on status response codes from origin. I have written a Perlbal plugin CacheKill specifically to address this issue. Perlbal::Plugin::CacheKill sits between backend services and Squid and rewrites cache-control headers based on response code.

Here is the setup: Client -> Squid -> Perlbal -> Backend Services

If the backend service responds with a 501,502 or 503 http status code, Perlbal will append Cache-Control: no-cache header before giving back the response to Squid.

Here is the configuration file for Perlbal::Plugin::CacheKill to deny Squid from caching error pages.


CREATE SERVICE fab40
  SET listen        = 0.0.0.0:8003
  SET role          = reverse_proxy
  SET pool          = backends
  cache_kill codes = 404,500,501,503,502
  SET plugins = CacheKill
ENABLE fab40

Stitching software together can make Squid just as flexible as Varnish with its VCL configuration.