{"id":33952175,"url":"https://github.com/alpshq/statamic-cache-evader","last_synced_at":"2025-12-12T19:26:47.802Z","repository":{"id":42043641,"uuid":"460662921","full_name":"alpshq/statamic-cache-evader","owner":"alpshq","description":"A statamic Addon to help you evade the static cache and support forms on cached pages.","archived":false,"fork":false,"pushed_at":"2023-09-21T16:43:14.000Z","size":157,"stargazers_count":4,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-14T13:53:29.529Z","etag":null,"topics":["statamic-addon"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alpshq.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-02-18T01:01:59.000Z","updated_at":"2025-02-08T22:10:54.000Z","dependencies_parsed_at":"2022-07-15T07:17:02.907Z","dependency_job_id":null,"html_url":"https://github.com/alpshq/statamic-cache-evader","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/alpshq/statamic-cache-evader","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpshq%2Fstatamic-cache-evader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpshq%2Fstatamic-cache-evader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpshq%2Fstatamic-cache-evader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpshq%2Fstatamic-cache-evader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alpshq","download_url":"https://codeload.github.com/alpshq/statamic-cache-evader/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpshq%2Fstatamic-cache-evader/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27689539,"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","status":"online","status_checked_at":"2025-12-12T02:00:06.775Z","response_time":129,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["statamic-addon"],"created_at":"2025-12-12T19:26:46.988Z","updated_at":"2025-12-12T19:26:47.794Z","avatar_url":"https://github.com/alpshq.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- statamic:hide --\u003e\n# Cache Evader\n\n\u003e Cache evasion for Statamic 3\n\n\u003c!-- /statamic:hide --\u003e\n\nThis Addon provides various simple ways to serve **uncached content** on **cached pages** and makes it possible to use Statamic forms on cached pages.\n\n## What you can do\n\n- [Serve whole uncached pages based on an HTTP GET parameter](https://github.com/alpshq/statamic-cache-evader#usage-cache-evading-based-on-http-get-parameter)\n- [Use Statamic forms on cached pages](https://github.com/alpshq/statamic-cache-evader#usage-forms)\n- **NEW**: [Inject uncached partials as part of cached pages](https://github.com/alpshq/statamic-cache-evader#usage-inject-uncached-partials-as-part-of-cached-pages)\n\n## Support\n\nIf you like the Addon consider [following me on Twitter](https://twitter.com/jakub_jo). If you've feature requests, feel free to start a discussion by opening a GitHub issue. If you've any further questions or want to discuss an opportunity with me, drop me a line at [hello@jakub.io](mailto:hello@jakub.io). \n\n## Installation\n\nYou can install the addon using composer:\n\n```\ncomposer require alpshq/statamic-cache-evader\n```\n\u003c!-- statamic:hide --\u003e\n\nAlternatively you can install the addon by navigating to [Statamic's marketplace](https://statamic.com/addons/alps/cache-evader) within your Control Panel and searching there for `Cache Evader`.\n\n\u003c!-- /statamic:hide --\u003e\n\n## Usage: Cache evading based on HTTP GET parameter\n\nEssentially any URL to which you add the GET parameter `_nc` with *any* value will evade the cache.\n\n### Modifier\n\nTo add the corresponding HTTP parameter name to any URL you can use the built-in modifier [evade_cache](src/Modifiers/EvadeCache.php):\n\n```html\n\u003ca href=\"{{ current_url | evade_cache }}\"\u003eLink to current (uncached) URL\u003c/a\u003e\n```\n\n#### How does the cache evading work?\n\n- The Addon will replace Statamic's default Cache middleware with the Addon's [StaticCache](src/Http/Middleware/StaticCache.php) middleware.\n- The replaced middleware will check if a cache evading parameter is part of the request. If a parameter is found, the cache is evaded. If not, Statamic default behavior applies.\n\n## Usage: Forms\n\nTo make your forms work in cached environments make sure to add the [{{ cache_evader_scripts }}](src/Tags/CacheEvaderScripts.php) tag right before the closing `\u003c/body\u003e` tag. Add it either on every page which contains forms or add it to your layout globally:\n\n```html\n\u003c!doctype html\u003e\n\u003chtml\u003e\n    \u003chead\u003e\n        ...\n    \u003c/head\u003e\n    \u003cbody\u003e\n        {{ template_content }}\n        ...\n        {{ cache_evader_scripts }}\n    \u003c/body\u003e\n\u003c/html\u003e\n```\n\nThe tag will load a script which will add a hidden input field with the name `_xsrf_token` and the value of the XSRF cookie to all your forms.\n\nThe [SpoofXsrfHeader](src/Http/Middleware/SpoofXsrfHeader.php) middleware will make sure the added field gets validated by the default Laravel CSRF protection middleware. \n\nIn order to get meaningful error and success messages for your users you need to make sure the forms redirect to an **uncached** page.\nWithin your forms add `_redirect` and `_error_redirect` links containing the cache evading HTTP parameter mentioned in the beginning. The simplest way to achieve this is by using the `evade_cache` modifier:\n\n```html\n{{ form:create handle=\"contact-form\" }}\n    \u003cinput type=\"hidden\" name=\"_redirect\" value=\"{{ current_url | evade_cache }}\" /\u003e\n    \u003cinput type=\"hidden\" name=\"_error_redirect\" value=\"{{ current_url | evade_cache }}\" /\u003e\n    \u003c!-- Your fields, buttons, success \u0026 error messages ... --\u003e \n{{ /form:create }}\n```\n\nAfter a submission, the above form will redirect you to the uncached version of the current page displaying all dynamic content, such as error and success messages. \n\nThat's it. Your forms will work as if there was no cache at all.\n\n### How does it work in detail?\n\n- The [script](resources/js/app.js) which you pull in with the [{{ cache_evader_scripts }}](src/Tags/CacheEvaderScripts.php) tags will loop through all your forms and add the current `XSRF-TOKEN` cookie value to the form by appending a hidden input field with the name `_xsrf_token`.\n- If no such Cookie exists (This is especially the case when you're using the `full` cache strategy and the current user was served by the static file cache):\n    - The script will make a lightweight fetch request to the `cache-evader.ping` route (`/cache-evader/ping`). \n    - Laravel will respond with the `XSRF-TOKEN` cookie attached. \n    - All following visits and requests have access to the `XSRF-TOKEN` cookie and no further fetch request will be made.\n- The [`SpoofXsrfHeader`](src/Http/Middleware/SpoofXsrfHeader.php) middleware will populate the request's `x-xsrf-token` header with the value of the `_xsrf_token` field. It pretty much reproduces current SPA behavior, which is [mentioned in the Laravel documentation](https://laravel.com/docs/9.x/csrf#csrf-x-xsrf-token). Inspiration came from Laravel's [form method spoofing](https://laravel.com/docs/9.x/routing#form-method-spoofing)\n\n## Usage: Inject uncached partials as part of cached pages\n\nWouldn't it be fine if you could utilize Statamic's full caching strategy while displaying **dynamic content** on your pages?\n\nLook no further -- you've found the solution. With the help of **uncached** partials inside your **cached** pages you can get the best out of both worlds.\n\n### Setting it up\n\nTo enable injecting of custom partials make sure to add the [{{ cache_evader_scripts }}](src/Tags/CacheEvaderScripts.php) tag right before the closing `\u003c/body\u003e` tag. Add it either on every page on which you'll use the `{{ cache_evader_partial }}` tag or add it to your layout globally:\n\n```html\n\u003c!doctype html\u003e\n\u003chtml\u003e\n    \u003chead\u003e\n        ...\n    \u003c/head\u003e\n    \u003cbody\u003e\n        {{ template_content }}\n        ...\n        {{ cache_evader_scripts }}\n    \u003c/body\u003e\n\u003c/html\u003e\n```\n\nThe tag will load a script which will fetch the contents of your uncached partials by sending an immediate `fetch` request for each partial. The fetch request will evade the cache and load any dynamic content you specifiy in your partials.\n\n### Basic Usage\n\nFirst things first: Create a simple partial which contains dynamic content: `partials/user.antlers.html`.\n\n```html\n\u003c!-- Your partial with dynamic content --\u003e\n{{ if logged_in }}\n    Welcome {{ current_user:email }}\n{{ else }}\n    Please login.\n{{ /if }}\n```\n\nNow simply include the partial in your template using the `{{ cache_evader_partial }}` tag:\n\n```html\n\u003c!-- Your template --\u003e\n{{ cache_evader_partial:user }}\n\u003c!--                    ^^^^ -\u003e This is the file name of the partial. --\u003e\n```\n\n**That's it.** Your **fully cached page** will now **always** display your user's email!\n\n### Parameters\n\nYou can add any number of parameters to your partial. You can access the parameters as regular variables in your partial. \n\n\u003e **Important:** \\\n\u003e Keep in mind, parameters are publicly visible -- don't share secrets using parameters!\n\n```html\n\u003c!-- Your partial with dynamic content --\u003e\n{{ if logged_in }}\n    Welcome {{ current_user:email }}\n{{ else }}\n    Please \u003ca href=\"{{ login_url }}\"\u003elogin\u003c/a\u003e.\n{{ /if }}\n```\n\n```html\n\u003c!-- Your template --\u003e\n{{ cache_evader_partial:user login_url=\"/login\" }}\n```\n\n### Displaying a loading message\n\nYou can display a loading message or an indicator. Simply wrap your loading indicator in the tag pair:\n\n```html\n\u003c!-- Your template --\u003e\n{{ cache_evader_partial src=\"user\" login_url=\"/login\" }}\n    Loading login state ...\n{{ /cache_evader_partial }}\n```\n\n### Placeholder element\n\nWhen you include a partial using the `{{ cache_evader_partial }}` tag, a placeholder `div` will be rendered instead of the partial. The placeholder is eventually swaped out with the content of your partial.\n\nYou can change the placeholder by adding a `wrap` parameter: `{{ cache_evader_partial src=\"...\" wrap=\"span\" }}`.\n\n### Wrapping of your partial's content\n\nYour partial's content will be wrapped in a `div` element. You can avoid this behaviour by rendering a single root element in your partial.\n\n**This will be wrapped in a `div`:**\n```html\n\u003cspan\u003e\n    Welcome {{ current_user:email }}!\n\u003c/span\u003e\n\u003ca href=\"{{ logout_url }}\"\u003eNot {{ current_user:email }}?\u003c/a\u003e\n```\n\n**This will NOT be wrapped in a `div`:**\n```html\n\u003cp\u003e\n    \u003cspan\u003e\n        Welcome {{ current_user:email }}!\n    \u003c/span\u003e\n    \u003ca href=\"{{ logout_url }}\"\u003eNot {{ current_user:email }}?\u003c/a\u003e\n\u003c/p\u003e\n```\n\n### Script tags\n\nYes! You can include script tags in your partials. They'll be executed.\n\n```html\n\u003c!-- Your partial with dynamic content --\u003e\n{{ if logged_in }}\n    Welcome {{ current_user:email }}\n{{ else }}\n    Please \u003ca href=\"{{ login_url }}\"\u003elogin\u003c/a\u003e.\n{{ /if }}\n\n\u003cscript src=\"{{ mix src='/js/user.js' }}\"\u003e\u003c/script\u003e\n```\n\n### JavaScript Hooks\n\n#### Before a fetch request is sent\n\nBefore each fetch request is sent to your server the `cacheEvaderBeforeInject` event is triggered on the placeholder element. You can cancel the fetch request by invoking `preventDefault()` on the event.\n\n```js\nwindow.addEventListener('cacheEvaderBeforeInject', ev =\u003e {\n  ev.preventDefault(); // No fetch request is sent.\n  // ev.target -- The placeholder element\n  // ev.detail -- See below which properties are available. \n});\n```\n\nThe event will have a `detail` property which contains the url to which the request is sent and also all the parameters you've supplied to the partial.\n\n| Name | Type | Purpose |\n| :--- | :--- | :--- |\n| url | `string` | The URL which will render the partial's contents | \n| params | `object` | An object which contains all the parameters you've supplied to the partial |\n| params.view | `string` | The path to the partial |\n| params.signature | `string` | Laravel's URL signature |\n\n#### After the content was injected\n\nAfter the dynamic content of your partial was fetched \u0026 injected into the DOM the `cacheEvaderAfterInject` event is triggered on the injected element.\n\n```js\nwindow.addEventListener('cacheEvaderAfterInject', ev =\u003e {\n  // ev.target -- See below what the target will be.\n  // ev.detail -- See below which properties are available.\n});\n```\n\nThe value of the event `target` will be the wrapping element of your partial. If your partial does have a single root element, the value of `target` will be your root element. Otherwise it'll be a wrapping `div`.\n\nThe event will have a `detail` property which contains the url to which the request was sent to, all the parameters you've supplied to the partial and the server's response.\n\n| Name | Type | Purpose |\n| :--- | :--- | :--- |\n| response | `Response` | The response object |\n| url | `string` | The URL which will render the partial's contents | \n| params | `object` | An object which contains all the parameters you've supplied to the partial |\n| params.view | `string` | The path to the partial |\n| params.signature | `string` | Laravel's URL signature |\n\n### How do dynamic partials work in detail?\n\n- When using the `{{ cache_evader_partial }}` tag, a placeholder will be rendered with no actual content.\n- The JavaScript you add to the browser using the `{{ cache_evader_scripts }}` tag will iterate over each placeholder and will send a fetch request to the server\n- Your server renders the partial and sends it to the browser\n- The placeholder will be swaped with the actual content\n\n## Configuration\n\nYou can publish the configuration file to modify the default parameter name (`_nc`), and the default value (`!`):\n\n```\nphp artisan vendor:publish --tag=cache-evader-config\n```\n\nYou'll find the published [configuration file](config/cache-evader.php) in `config/statamic/cache-evader.php` -- review it for explanation about the various options.\n\n## Security\n\nIf you encounter any security related issues, please email directly jakub@alps.dev instead of opening an issue. All security related issues will be promptly addressed.\n\n## License\n\nMIT -- see the [license file](LICENSE.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falpshq%2Fstatamic-cache-evader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falpshq%2Fstatamic-cache-evader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falpshq%2Fstatamic-cache-evader/lists"}