{"id":18773121,"url":"https://github.com/webfactory/symfony-http-caching-demo","last_synced_at":"2025-04-13T09:07:25.552Z","repository":{"id":72200620,"uuid":"222900680","full_name":"webfactory/symfony-http-caching-demo","owner":"webfactory","description":"Demo application/fiddle to play around with Symfony and HTTP Caching","archived":false,"fork":false,"pushed_at":"2020-06-09T17:24:28.000Z","size":294,"stargazers_count":4,"open_issues_count":2,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-13T09:07:18.432Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/webfactory.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-11-20T09:32:08.000Z","updated_at":"2021-06-08T15:42:00.000Z","dependencies_parsed_at":"2023-05-31T08:00:34.466Z","dependency_job_id":null,"html_url":"https://github.com/webfactory/symfony-http-caching-demo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webfactory%2Fsymfony-http-caching-demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webfactory%2Fsymfony-http-caching-demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webfactory%2Fsymfony-http-caching-demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webfactory%2Fsymfony-http-caching-demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/webfactory","download_url":"https://codeload.github.com/webfactory/symfony-http-caching-demo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248688567,"owners_count":21145766,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-07T19:32:54.088Z","updated_at":"2025-04-13T09:07:25.545Z","avatar_url":"https://github.com/webfactory.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Symfony HTTP Caching Sandbox/Fiddle\n\nThis repo contains a basic Symfony Application that can be used to \ndemonstrate how to do HTTP Caching with Symfony. It also contains the \nmaterials (very few slides) that I used to speak on the topic at the\n[SymfonyCon Amsterdam 2019](https://amsterdam2019.symfony.com/). \n\n## About Me\n\nMy name is Matthias Pigulla. I lead the team at webfactory GmbH ([GitHub](https://github.com/webfactory), [Website](https://www.webfactory.de/), [Twitter](https://twitter.com/webfactory)) in Bonn, Germany,\nwhere we do contract-based software development for various clients. Symfony is our\nmain and favorite platform that we have been using since the pre-release of 2.0 back in 2011. \n\nYou can find my personal profiles on [GitHub](https://github.com/mpdude), [Twitter](https://twitter.com/mpdude_de) on [SymfonyConnect](https://connect.symfony.com/profile/mpdude).\n\n## Using this\n\nYou should be able to clone this repo, run `composer install` and afterwards start the PHP\nembedded server with `bin/console server:start 0.0.0.0:8000`. \n\n## Quick Reference\n\nVery short, rough descriptions for the headers and `Cache-Control` values covered. See [RFC 7234](https://tools.ietf.org/html/rfc7234) and related \nspecs for authoritative information.\n\n`private`\n: Response must not be stored in a shared cache. Private caches may store it, even if normally non-cacheable.\n\n`no-cache`\n: Response must not be used from cache without successful revalidation on the origin server.\n\n`must-revalidate`\n: Once the response has become stale, it must not be used from cache without successful revalidation on the origin server.\n\n`no-store`\n: Response must not be cached on permanent storage and caches should do their best to remove contents from memory etc. as soon as possible  \n\n`max-age`\n: Response lifetime (freshness) in seconds\n\n`Age`\n: Must be present when response has been served from cache. Conveys the time in seconds the response has already been in the cache.\n\n`ETag`\n: A token for revalidation, opaque (with no particular meaning) to the user-agent.\n\n`Last-Modified`\n: Time the resource was last changed on the origin server. Can be used (with 1 second precision) for revalidation and allows for \"heuristic freshness\" to be applied when not other expiration time was given and status codes that are either cacheable by default or caching has been explicitly allowed. \n\n`Vary`\n: Lists additional HTTP request headers whose values must be used to key cached responses. \n\n`public`\n: Explicitly defines a response as cacheable in shared caches. Even overrides special ruling if an `Authorization` header is present, so use cautiously.\n \n## About the Talk\n\nSymfonyCon finally gave me the opportunity to bring a bit more structure into what I \nhave already been showing people before. This talk has been designed for the \"Beginners\"\ntrack and so, apart from having some basic Symfony knowledge and maybe HTTP basics (what are\nrequest methods, what are headers), you need not know anything about HTTP Caching in particular.\n\nI have been trying to structure it in a way that allows me to progress from simple to more\ncomplex, adding only one new concept at a time. Live demonstration and coding hopefully\nwill make it more comprehensible to the audience. Also, repetitions of previous concepts\nshuold help to make things stick.\n\n### Things covered:\n\n* What is an HTTP Cache and what does it do?\n* Cache layers and `public` vs. `private` caches\n* How browsers behave as to caching and how you can use `curl` to debug\n* Introduce `private`, `max-age` expiration-based caching; explain \"fresh\" and \"stale\"\n* Introduce validation by adding `ETag` and `Last-Modified`, demoing conditional requests\n* Optimize controller code by short-cutting the response on 304.\n* Mention `must-revalidate` and heuristic expiration \n* Make expiration more explicit with either `no-cache` or `max-age`, this time combining expiration and validation\n* Introduce the `HttpCache` implementation \n* Show `public` expiration caching and explain the cache trace as well as `Age` headers\n* Show `public` caching with expiration and revalidation\n* Warn on the implications of `public`, at least mention `Vary`.\n* Introduce Egde Side Includes (ESI) and explain how this can be useful\n* Show how ESI can be activated and used from Twig\n* Show response body including `\u003cesi:include\u003e` markup\n* Demo a showcase where `private` and `public` content with different expiration times can be mixed, cached and validated.\n\n### Outtakes\n\nThings I would have 😍 to include but was unable to due to being limited to 45 minutes:\n\n* Work test-based by introducing `behat` and Mink/Goutte. Write scenarios as well as context steps for everything.\n* Show how the Asset Component can be used to make assets cacheable, including `immutable` and cache busting/revving techniques\n* Tell about `ab` and use it to show off performance improvements when using the `HttpCache`\n* Show how to configure Apache Logging to record cache hit/miss rates. Use `grep`, `awk`, `sort` and `uniq` to find URLs/controllers that could benefit from caching and/or have bad hit rates. (https://symfony.com/blog/new-in-symfony-4-3-improved-httpcache-logging)\n* Include a controller to demonstrate `Vary`, showing how requests e. g. with different cookie values can be separated.\n* Show how to change Symfony environment, debug and caching by means of cookies (https://github.com/symfony/recipes/pull/679)\n\nBits and pieces for that, without further explanation:\n\n#### Apache configuration\n\n```\nRewriteEngine On\nRewriteCond %{DOCUMENT_ROOT}$1/$2 -f\nRewriteRule ^(.*)/assets-version-\\d+/(.*)$ $1/$2 [END,E=FARFUTURE_ASSET:1]\nHeader set Cache-Control \"max-age=290304000, immutable, public\" env=FARFUTURE_ASSET\nHeader set Last-Modified \"Fri, 01 Jan 1988 00:00:00 GMT\" env=FARFUTURE_ASSET\nHeader set Expires \"Thu, 01 Jan 2037 00:00:00 GMT\" env=FARFUTURE_ASSET\nHeader unset ETag env=FARFUTURE_ASSET\n\nLogFormat \"[%{%Y-%m-%dT%H:%M:%S}t.%{msec_frac}t%{%z}t] \\\"%r\\\" %\u003es %b %{UNIQUE_ID}e proc_time=%D cache_status=\\\"%{cache_status}n\\\"\" cache_log\nCustomLog     /var/www/symfony-http-caching/logs/cache_log cache_log\nHeader always note X-Cache cache_status\nHeader always unset X-Cache\n``` \n\n#### Log analysis\n\n```\n# Can be used for cache hit/miss, response codes, request methods...\ncat logs/cache_log | awk 'BEGIN { FPAT = \"([^ ]+)|(\\\"[^\\\"]+\\\")\" } { print $2 \"\\t\" $3; }' | sort | uniq -c\n``` \n\n#### Benchmarking\n\n```\nab -c 5 -n 100 'http://127.0.0.1:8000/'\nab -c 5 -n 100 -C 'SYMFONY_CACHE=1' 'http://127.0.0.1:8000/'\n```\n\n#### `Vary` controller\n\n```php\n    /**\n     * @Route(\"/vary\")\n     * @Cache(public=true, maxage=60)\n     */\n    public function vary(Request $request): Response\n    {\n        $response = new Response($request-\u003ecookies-\u003egetAlnum('demo') . \"\\n\");\n        $response-\u003esetVary('Cookie', false);\n\n        return $response;\n    }\n```\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebfactory%2Fsymfony-http-caching-demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwebfactory%2Fsymfony-http-caching-demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebfactory%2Fsymfony-http-caching-demo/lists"}