Moved!

Just writing to let you know that I’ve switched servers for this blog! I’ve been meaning to get this on to a faster server that I have control over for awhile, and I finally got around to doing it.

For those of you who are curious, this site is being served by WordPress Multisite, on a Sakura 2G VPS.

And because everyone likes talking about stacks:

Stack.

Kidding.

That’s it! There is some moderate caching in most layers (WordPress Object Cache, APC, NGINX, etc), but nothing too drastic. It’s much faster now than it was on the previous server – we’ll see how this one holds up.

Thoughts during the Migration Process

MariaDB

I can’t say this enough – really, really, really smooth. Just stop mysql, remove, install MariaDB, then start it up again. All my data was migrated automatically.

NGINX + PHP + WordPress

There were a ton of guides for this kind of setup, but the two that most helped me were the WordPress Codex and rtCamp’s excellent WordPress + Nginx tutorials. Reading through both is highly recommended.

Varnish: Debrief

Today, we just released the Varnish-ed site I posted about a few days ago. A few things to be careful about in your Varnish deployment (that I had originally overlooked):

  • Make sure you’re 301 redirecting traffic to your canonical name (http://www.example.com/example to http://example.com/example, or vice versa)
  • Do this redirecting within Varnish! I had strange problems when applying this logic within Apache.
  • If you insist on applying the redirection logic within Apache, you may run into a problem where the Varnish health-check “probes” will fail (and deem your backend “sick”, throwing 503 errors at everyone after the grace period has expired).

Varnish on a Dynamic Site

TL;DR: Scroll to the bottom for the attached VCL.

No, I’m not talking about the stuff you put on paint to make it last longer.

I’m talking about this amazing piece of software: https://www.varnish-cache.org/

Put simply, Varnish is a “reverse proxy” – a piece of software that goes between the main server and the client. Basically what it does is caches content, so PHP (or whatever backend you’re using) doesn’t need to run for every single request – this makes perfect sense with static content. For sites like WordPress, filled with mostly static content, Varnish will work out of the box (minor configuration changes are required for small things).

Using Varnish with mixed-static and dynamic content is a little harder, though. Recently, we were hired to optimize a client’s website. This particular website was written on a particularly horribly-written framework (it looked like some proprietary framework – not something I want to dissect and analyze for performance). For an example of how awful this framework was, it took about 10 seconds (yes, seconds) for the server to render the index page. After doing as much optimization as possible without dissecting, I was able to cut the time down to about 2 seconds.

2 seconds is still an eternity.

So, I decided to use Varnish. With a big caveat: it only caches content if you aren’t logged in. Since the framework I was working with was intent on setting the PHPSESSID cookie on every page load, I couldn’t just tell Varnish to cache when that cookie was present. Instead, when the user logs in, a LOGIN cookie is set, and Varnish uses this cookie to determine whether to cache the content or not. Logged-in users still have the awful user experience of having to wait 2 seconds for each page to load, but at least people who are just coming to look around have a decent experience.

Here are the relevant sections of the VCL file that enables this logic:

sub vcl_fetch {
  if (!(beresp.http.Set-Cookie ~ "LOGIN") && !(req.http.cookie ~ "LOGIN")) {
    unset beresp.http.Pragma;
    unset beresp.http.Set-Cookie;
    set beresp.http.Cache-Control = "public; max-age=1800";
    unset beresp.http.Expires;
    set beresp.ttl = 30m;
    return (deliver);
  }
}

sub vcl_recv {
  if (req.http.Cookie ~ "LOGIN") {
    set req.http.Cookie = ";" req.http.Cookie;
    set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(PHPSESSID|LOGIN)=", "; 1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
    set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
    if (req.http.Cookie == "") {
      remove req.http.Cookie;
    }
  } else {
    remove req.http.Cookie;
  }
}