Skip to content
  • About
    • What is Symfony?
    • Community
    • News
    • Contributing
    • Support
  • Documentation
    • Symfony Docs
    • Symfony Book
    • Screencasts
    • Symfony Bundles
    • Symfony Cloud
    • Training
  • Services
    • Platform.sh for Symfony Best platform to deploy Symfony apps
    • SymfonyInsight Automatic quality checks for your apps
    • Symfony Certification Prove your knowledge and boost your career
    • SensioLabs Professional services to help you with Symfony
    • Blackfire Profile and monitor performance of your apps
  • Other
  • Blog
  • Download
sponsored by
  1. Home
  2. Documentation
  3. Http Cache
  4. Cache Invalidation

Cache Invalidation

Edit this page

"There are only two hard things in Computer Science: cache invalidation and naming things." -- Phil Karlton

Once a URL is cached by a gateway cache, the cache will not ask the application for that content anymore. This allows the cache to provide fast responses and reduces the load on your application. However, you risk delivering outdated content. A way out of this dilemma is to use long cache lifetimes, but to actively notify the gateway cache when content changes. Reverse proxies usually provide a channel to receive such notifications, typically through special HTTP requests.

Caution

While cache invalidation is powerful, avoid it when possible. If you fail to invalidate something, outdated caches will be served for a potentially long time. Instead, use short cache lifetimes or use the validation model, and adjust your controllers to perform efficient validation checks as explained in HTTP Cache Validation.

Furthermore, since invalidation is a topic specific to each type of reverse proxy, using this concept will tie you to a specific reverse proxy or need additional efforts to support different proxies.

Sometimes, however, you need that extra performance you can get when explicitly invalidating. For invalidation, your application needs to detect when content changes and tell the cache to remove the URLs which contain that data from its cache.

Tip

If you want to use cache invalidation, have a look at the FOSHttpCacheBundle. This bundle provides services to help with various cache invalidation concepts and also documents the configuration for a couple of common caching proxies.

If one content corresponds to one URL, the PURGE model works well. You send a request to the cache proxy with the HTTP method PURGE (using the word "PURGE" is a convention, technically this can be any string) instead of GET and make the cache proxy detect this and remove the data from the cache instead of going to the application to get a response.

Here is how you can configure the Symfony reverse proxy to support the PURGE HTTP method. First create a caching kernel that overrides the invalidate() method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// src/CacheKernel.php
namespace App;

use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
// ...

class CacheKernel extends HttpCache
{
    protected function invalidate(Request $request, bool $catch = false): Response
    {
        if ('PURGE' !== $request->getMethod()) {
            return parent::invalidate($request, $catch);
        }

        if ('127.0.0.1' !== $request->getClientIp()) {
            return new Response(
                'Invalid HTTP method',
                Response::HTTP_BAD_REQUEST
            );
        }

        $response = new Response();
        if ($this->getStore()->purge($request->getUri())) {
            $response->setStatusCode(Response::HTTP_OK, 'Purged');
        } else {
            $response->setStatusCode(Response::HTTP_NOT_FOUND, 'Not found');
        }

        return $response;
    }
}

Then, register the class as a service that decorates http_cache:

1
2
3
4
5
6
7
8
9
10
11
12
13
// src/CacheKernel.php
namespace App;

// ...
use Symfony\Component\DependencyInjection\Attribute\AsDecorator;
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;

#[Autoconfigure(bind: ['$surrogate' => '@?esi'])]
#[AsDecorator(decorates: 'http_cache')]
class CacheKernel extends HttpCache
{
    // ...
}
1
2
3
4
5
6
7
8
# config/services.yaml
services:
    App\CacheKernel:
        decorates: http_cache
        arguments:
            - '@kernel'
            - '@http_cache.store'
            - '@?esi'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        https://symfony.com/schema/dic/services/services-1.0.xsd"
>
    <services>
        <service id="App\CacheKernel" decorates="http_cache">
            <argument type="service" id="kernel"/>
            <argument type="service" id="http_cache.store"/>
            <argument type="service" id="esi" on-invalid="null"/>
        </service>
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// config/services.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use App\CacheKernel;

return function (ContainerConfigurator $container): void {
    $services = $container->services();

    $services->set(CacheKernel::class)
        ->decorate('http_cache')
        ->args([
            service('kernel'),
            service('http_cache.store'),
            service('esi')->nullOnInvalid(),
        ])
    ;
};

Danger

You must protect the PURGE HTTP method somehow to avoid random people purging your cached data.

Purge instructs the cache to drop a resource in all its variants (according to the Vary header, see Varying the Response for HTTP Cache). An alternative to purging is refreshing the content. Refreshing means that the caching proxy is instructed to discard its local cache and fetch the content again. This way, the new content is already available in the cache. The drawback of refreshing is that variants are not invalidated.

In many applications, the same content bit is used on various pages with different URLs. More flexible concepts exist for those cases:

  • Banning invalidates responses matching regular expressions on the URL or other criteria;
  • Cache tagging lets you add a tag for each content used in a response so that you can invalidate all URLs containing a certain content.
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version

    Symfony 7.1 is backed by

    Code consumes server resources. Blackfire tells you how

    Code consumes server resources. Blackfire tells you how

    Be trained by SensioLabs experts (2 to 6 day sessions -- French or English).

    Be trained by SensioLabs experts (2 to 6 day sessions -- French or English).

    Version:

    Symfony footer

    Avatar of adursun, a Symfony contributor

    Thanks adursun for being a Symfony contributor

    2 commits • 4 lines changed

    View all contributors that help us make Symfony

    Become a Symfony contributor

    Be an active part of the community and contribute ideas, code and bug fixes. Both experts and newcomers are welcome.

    Learn how to contribute

    Symfony™ is a trademark of Symfony SAS. All rights reserved.

    • What is Symfony?

      • What is Symfony?
      • Symfony at a Glance
      • Symfony Components
      • Symfony Releases
      • Security Policy
      • Logo & Screenshots
      • Trademark & Licenses
      • symfony1 Legacy
    • Learn Symfony

      • Symfony Docs
      • Symfony Book
      • Reference
      • Bundles
      • Best Practices
      • Training
      • eLearning Platform
      • Certification
    • Screencasts

      • Learn Symfony
      • Learn PHP
      • Learn JavaScript
      • Learn Drupal
      • Learn RESTful APIs
    • Community

      • Symfony Community
      • SymfonyConnect
      • Events & Meetups
      • Projects using Symfony
      • Contributors
      • Symfony Jobs
      • Backers
      • Code of Conduct
      • Downloads Stats
      • Support
    • Blog

      • All Blog Posts
      • A Week of Symfony
      • Case Studies
      • Cloud
      • Community
      • Conferences
      • Diversity
      • Living on the edge
      • Releases
      • Security Advisories
      • Symfony Insight
      • Twig
      • SensioLabs Blog
    • Services

      • SensioLabs services
      • Train developers
      • Manage your project quality
      • Improve your project performance
      • Host Symfony projects

      Powered by

    Follow Symfony