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. Working with Server Side Includes

Working with Server Side Includes

Edit this page

In a similar way as ESI (Edge Side Includes), SSI can be used to control HTTP caching on fragments of a response. The most important difference that is SSI is known directly by most web servers like Apache, Nginx etc.

The SSI instructions are done via HTML comments:

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html>
    <body>
        <!-- ... some content -->

        <!-- Embed the content of another page here -->
        <!--#include virtual="/..." -->

        <!-- ... more content -->
    </body>
</html>

There are some other available directives but Symfony manages only the #include virtual one.

Danger

Be careful with SSI, your website may fall victim to injections. Please read this OWASP article first!

When the web server reads an SSI directive, it requests the given URI or gives directly from its cache. It repeats this process until there is no more SSI directives to handle. Then, it merges all responses into one and sends it to the client.

Using SSI in Symfony

First, to use SSI, be sure to enable it in your application configuration:

1
2
3
# config/packages/framework.yaml
framework:
    ssi: { enabled: true }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- config/packages/framework.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/symfony"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:framework="http://symfony.com/schema/dic/symfony"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        https://symfony.com/schema/dic/services/services-1.0.xsd
        http://symfony.com/schema/dic/symfony
        https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">

    <framework:config>
        <framework:ssi enabled="true"/>
    </framework:config>
</container>
1
2
3
4
5
6
7
8
// config/packages/framework.php
use Symfony\Config\FrameworkConfig;

return static function (FrameworkConfig $framework): void {
    $framework->ssi()
        ->enabled(true)
    ;
};

Suppose you have a page with private content like a Profile page and you want to cache a static GDPR content block. With SSI, you can add some expiration on this block and keep the page private:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// src/Controller/ProfileController.php
namespace App\Controller;

use Symfony\Component\HttpKernel\Attribute\Cache;
// ...

class ProfileController extends AbstractController
{
    public function index(): Response
    {
        // by default, responses are private
        return $this->render('profile/index.html.twig');
    }

    #[Cache(smaxage: 600)]
    public function gdpr(): Response
    {
        return $this->render('profile/gdpr.html.twig');
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// src/Controller/ProfileController.php
namespace App\Controller;

// ...
class ProfileController extends AbstractController
{
    public function index(): Response
    {
        // by default, responses are private
        return $this->render('profile/index.html.twig');
    }

    public function gdpr(): Response
    {
        $response = $this->render('profile/gdpr.html.twig');

        // sets to public and adds some expiration
        $response->setSharedMaxAge(600);

        return $response;
    }
}

The profile index page has not public caching, but the GDPR block has 10 minutes of expiration. Let's include this block into the main one:

1
2
3
4
5
6
7
{# templates/profile/index.html.twig #}

{# you can use a controller reference #}
{{ render_ssi(controller('App\\Controller\\ProfileController::gdpr')) }}

{# ... or a path (in server's SSI configuration is common to use relative paths instead of absolute URLs) #}
{{ render_ssi(path('profile_gdpr')) }}

The render_ssi twig helper will generate something like:

1
<!--#include virtual="/_fragment?_hash=abcdef1234&_path=_controller=App\Controller\ProfileController::gdpr" -->

render_ssi ensures that SSI directive is generated only if the request has the header requirement like Surrogate-Capability: device="SSI/1.0" (normally given by the web server). Otherwise it will embed directly the sub-response.

Note

For more information about Symfony cache fragments, take a tour on the ESI documentation.

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

    Check Code Performance in Dev, Test, Staging & Production

    Check Code Performance in Dev, Test, Staging & Production

    Make sure your project is risk free

    Make sure your project is risk free

    Version:
    • Using SSI in Symfony

    Symfony footer

    Avatar of Ilia Lazarev, a Symfony contributor

    Thanks Ilia Lazarev (@ilzrv) for being a Symfony contributor

    1 commit • 2 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