This piece of example code uses the Silex framework to illustrate how you can leverage HTTP to develop cacheable sites.
The code uses the following HTTP concepts:
- The use of
Cache-Control
headers using directives likePublic
,Private
to decide which HTTP responses are cacheable and which are not - The use of
Cache-Control
headers using directives likeMax-Age
andS-Maxage
to determine how long HTTP responses can be cached Expires
headers as a fallback to control the time-to-live- Cache variations based on the
Vary
header - Conditional requests based on the
Etag
header - Returning an
HTTP 304
status code when content was successfully revalidated - Content negotiation and language selection based on the
Accept-Language
header - Block caching using Edge Side Includes and HInclude
The output that this example code generates is highly cacheable. We don't keep track of state using cookies and the proper Cache-Control
headers are used to store the output in an HTTP cache.
If a reverse caching proxy (like Varnish) is installed in front of this application, it will respect the time-to-live that was set by the application.
Reverse caching proxies will also create cache variations by respecting the Vary
header. A separate version of the response is stored in cache per language.
Non-cacheable content blocks will not cause a full miss on the page. These content blocks are loaded separately using ESI or HInclude. Either of those techniques load blocks as a subrequest.
ESI tags are rendered by the reverse proxy, HInclude tags are loaded client-side by the browser. If the code notices that there's no reverse caching proxy in front of the application, it will render the output inline, without ESI.
When you test this code, please have a look at the HTTP request headers and HTTP response headers. That's where the magic happens.
The minimum PHP version requirement is PHP 5.5.9
. All dependencies are loaded via Composer, the PHP package manager. Dependency definition happens in the composer.json file:
{
"require": {
"silex/silex": "^2.0",
"twig/twig": "^1.27",
"symfony/twig-bridge": "^3.1",
"symfony/translation": "^3.1"
}
}
Run composer install
to install these dependencies. They will be stored in the vendor folder. They are bootstrapped in index.php via require_once __DIR__ . '/../vendor/autoload.php';
If you use Apache as your webserver, there's an .htaccess file that routes all traffic for non-existent files and directories to index.php.
Please make sure that your webserver's document root points to the public folder where the index.php file is located.
The index.php file is the controller of the application. It reads HTTP input and generates HTTP output.
Routes are matched via $app->get()
callbacks.
Output is formatted using the Twig templating language. The views folder contains the template file for each route:
- footer.twig contains the footer template which returns a translated string and a timestamp
- header.twig contains the header template which also returns a translated string and a timestamp
- index.twig contains the main template where the header, footer, and navigation are loaded, either via ESI or via HInclude
- nav.twig contains the navigation template
To see the impact of this code, I would advise you to install Varnish. Varnish will respect the HTTP response headers that were set and will cache the output.
This is the minimum amount of VCL code you need to make this work:
vcl 4.0;
backend default {
.host = "localhost";
.port = "8080";
}
sub vcl_recv {
set req.http.Surrogate-Capability="key=ESI/1.0";
if ((req.method != "GET" && req.method != "HEAD") || req.http.Authorization) {
return (pass);
}
return(hash);
}
sub vcl_backend_response {
if(beresp.http.Surrogate-Control~"ESI/1.0") {
unset beresp.http.Surrogate-Control;
set beresp.do_esi=true;
return(deliver);
}
}
This piece of VCL code assumes that Varnish is installed on port 80 and your webserver on port 8080 on the same machine.
This repository and its code are part of the code examples that are featured in my book "Getting Started With Varnish Cache". It serves as a set of best practices that should encourage developers to control the cacheability of their sites themselves, instead of relying on infrastructure configuration.
More information about me:
- Visit my website: https://feryn.eu
- Learn more about my book: https://book.feryn.eu
- Follow me on Twitter: @ThijsFeryn
- Follow me on Instagram: @ThijsFeryn
- View my LinkedIn profile: http://linkedin.com/in/thijsferyn
- View my public speaking track record: https://talks.feryn.eu
- Read my blog: https://blog.feryn.eu