The receiver part:

sub vcl_recv {
  # Wordpress - cache everything except...
  if (!(req.url ~ "wp-admin|xmlrpc\.php|wp-(comments-post|login|activate|mail)\.php|&preview=true")) {
    unset req.http.cookie;
    return (hash);
  }

  # Happens before we check if we have this in cache already.
  # 
  # Typically you clean up the request here, removing cookies you don't need,
  # rewriting the request, etc.
}

The backend part:

sub vcl_backend_response {
  # Happens after we have read the response headers from the backend.
  # 
  # Here you clean the response headers, removing silly Set-Cookie headers
  # and other mistakes your backend does.

  # Wordpress - delete unnecessary before storing in cache
  if (!(bereq.url ~ "wp-admin|xmlrpc\.php|wp-(comments-post|login|activate|mail)\.php|&preview=true")) {
    unset beresp.http.set-cookie;
    unset beresp.http.cookie;
    unset beresp.http.expires;
    unset beresp.http.cache-control;
    unset beresp.http.pragma;
    set beresp.ttl = 1m;
    set beresp.grace = 1w;
  }
}

This parts makes the client retry if they got an 503 Service Unavailable. This is practical if one backend suddenly went down.

sub vcl_backend_error {
  if (beresp.status == 503) {
    return (retry);
  }
}

If you use SSL, this is almost required. Otherwise the default works well enough.

# Split plain and encrypted objects
sub vcl_hash {
  hash_data(req.url);
  if (req.http.host) {
    hash_data(req.http.host);
  } else {
    hash_data(server.ip);
  }

  # Use special internal SSL hash for https content
  # X-Forwarded-Proto is set to https by Pound
  if (req.http.X-Forwarded-Proto ~ "https") {
     hash_data(req.http.X-Forwarded-Proto);
  }

  return (lookup);
}

Without this we won't be able to cache completely.

sub vcl_hit {
  set req.http.grace = "full";
}