diff --git a/docs/api/response.md b/docs/api/response.md index d5c530a..d03e51a 100644 --- a/docs/api/response.md +++ b/docs/api/response.md @@ -167,6 +167,7 @@ Here's a list of the most common HTTP status codes: * `200 OK` * `301 Permanent Redirect` * `302 Found` (previously `302 Temporary Redirect`) +* `304 Not Modified` (see [HTTP caching](#http-caching) below) * `403 Forbidden` * `404 Not Found` * `500 Internal Server Error` @@ -205,6 +206,183 @@ by the HTTP protocol. It's not recommended to mess with these default headers unless you're sure you know what you're doing. +## HTTP caching + +HTTP caching can be used to significantly improve the performance of web +applications by reusing previously fetched resources. HTTP caching is a whole +topic on its own, so this section only aims to give a basic overview of how +you can leverage HTTP caches with X. For a more in-depth overview, we highly +recommend [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching). + +HTTP supports caching for certain requests by default. In any but the most basic +use cases, it's often a good idea to explicitly specify HTTP caching headers +as part of the HTTP response to have more control over the freshness lifetime +and revalidation behavior. + +### Cache-Control + +The [`Cache-Control`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) +response header can be used to control caching of responses by browsers and +shared caches such as proxies and CDNs. In its most basic form, you can use this +response header to control the lifetime of a cached response like this: + +```php +get('/user', function () { + $html = <<Hello Alice +HTML; + + return new Response( + 200, + [ + 'Content-Type' => 'text/html; charset=utf-8', + 'Cache-Control' => 'max-age=3600', + ], + $html + ); +}); +``` + +An HTTP request can be sent like this: + +```bash +$ curl -I http://localhost:8080/user +HTTP/1.1 200 OK +Cache-Control: max-age=3600 +… +``` + +### ETag + +The [`ETag`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) +response header can be used for conditional requests. This ensures the response +body only needs to be transferred when it actually changes. For instance, you +can build a hash (or some other arbitrary identifier) for your contents and +check if it matches the incoming +[`If-None-Match`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match) +request header for subsequent requests. If both values match, you can send a +[`304 Not Modified`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304) +response and omit the response body like this: + +```php +get('/user', function (Psr\Http\Message\ServerRequestInterface $request) { + // example body, would usually come from some kind of database + $html = <<Hello Alice +HTML; + + $etag = '"' . sha1($html) . '"'; + if ($request->getHeaderLine('If-None-Match') === $etag) { + return new Response( + 304, + [ + 'Cache-Control' => 'max-age=0, must-revalidate', + 'ETag' => $etag + ] + ); + } + + return new Response( + 200, + [ + 'Content-Type' => 'text/html; charset=utf-8', + 'Cache-Control' => 'max-age=0, must-revalidate', + 'ETag' => $etag + ], + $html + ); +}); +``` + +An HTTP request can be sent like this: + +```bash +$ curl http://localhost:8080/user +