Going crazy with caching

Caching pages of logged in users


PHP Benelux, January 28th, 2017


© David Buchmann

HTTP Caching

What is a reverse proxy again?

What about the real live?

Strategies when working with sessions

Avoid Sessions

Avoid Session

if (isset($_COOKIE[session_name()])) {
    setcookie(session_name(), '', time()-3600);
}
            

Cleanup Cookies: Remove all but session cookie

sub vcl_recv {
    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.http.Cookie == "") {
        remove req.http.Cookie;
    }
}
            

varnish-cache.org/trac/wiki/VCLExampleRemovingSomeCookies #RemovingallBUTsomecookies




Logic in the Frontend

Render User Specific Parts in Javascript

$(document).ready(function () {
    $("#labels-edit").show();
    $("#milestone-edit").show();
    $("#assignee-edit").show();
});
            

Logic in the Frontend




Ajax for User Specific Parts

Ajax

$(document).ready(function () {
    $.ajax({
        url: "/sidebar.html"
    }).done(function( html ) {
        $( "#sidebar" ).append( html );
    });
});
            

Ajax

Cache lookup despite cookies

Cache lookup despite cookies

builtin.vcl

                // builtin.vcl
                sub vcl_recv {
                    // ...
                    if (req.method != "GET" && req.method != "HEAD") {
                        /* We only deal with GET and HEAD by default */
                        return (pass);
                    }
                    if (req.http.Authorization || req.http.Cookie) {
                        /* Not cacheable by default */
                        return (pass);
                    }

                    return (hash);
                }
            

Cache lookup despite cookies

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

                    return (hash);
                }
            



Edge Side Includes

Use Edge Side Includes

Like server side include, but on Varnish:

ESI HTML

<html>
    <body>
        Main body.
        <esi:include src="fragment.php" />
    </body>
</html>
            
sub vcl_recv {
    // Add a Surrogate-Capability header
    // to announce ESI support.
    set req.http.Surrogate-Capability = "abc=ESI/1.0";
}
sub vcl_backend_response {
    // Check for ESI acknowledgement
    // and remove Surrogate-Control header.
    if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
        unset beresp.http.Surrogate-Control;
        set beresp.do_esi = true;
    }
}

ESI

Symfony has built-in ESI support

# app/config/config.yml
framework:
  esi: { enabled: true }
  fragments: { path: /_fragment }
            
Make sure either your webserver is only reachable from the Varnish server or add access restrictions for /_fragment
{# index.html.twig #}
{% render_esi(controller( 'AppBundle:Comments:comments', {'param': 42 })) %}
            

User Context

Introducing the User Context Hash

User Context


Mix solutions



Questions / Input / Feedback ?


Twitter: @dbu




Tools

FOSHttpCache

FOSHttpCacheBundle