Working with Edge Side Includes

Gateway caches are a great way to make your website perform better. But they have one limitation: they can only cache whole pages. If your pages contain dynamic sections, such as the user name or a shopping cart, you are out of luck. Fortunately, Symfony provides a solution for these cases, based on a technology called ESI, or Edge Side Includes. Akamai wrote this specification in 2001 and it allows specific parts of a page to have a different caching strategy than the main page.

ゲートウェイ キャッシュは、Web サイトのパフォーマンスを向上させる優れた方法です。ただし、1 つの制限があります。キャッシュできるのはページ全体だけです。ページにユーザー名やショッピング カートなどの動的なセクションが含まれていると、運が悪くなります。幸いなことに、Symfony は、ESI (Edge Side Includes) と呼ばれるテクノロジに基づいて、これらのケースに対するソリューションを提供します。 Akamai は 2001 年にこの仕様を作成し、ページの特定の部分にメイン ページとは異なるキャッシュ戦略を持たせることができます。

The ESI specification describes tags you can embed in your pages to communicate with the gateway cache. Only one tag is implemented in Symfony, include, as this is the only useful one outside of Akamai context:

ESI 仕様では、ゲートウェイ キャッシュと通信するためにページに埋め込むことができるタグについて説明しています。 Symfony に実装されているタグは 1 つだけ include です。これは、Akamai のコンテキスト外で唯一有用なタグです。
1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html>
    <body>
        <!-- ... some content -->

        <!-- Embed the content of another page here -->
        <esi:include src="http://..."/>

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

Note

ノート

Notice from the example that each ESI tag requires a fully-qualified URL. An ESI tag represents a page fragment that can be fetched via the given URL.

例から、各 ESI タグには完全修飾 URL が必要であることに注意してください。ESI タグは、指定された URL を介してフェッチできるページ フラグメントを表します。

When a request is handled, the gateway cache fetches the entire page from its cache or requests it from the backend application. If the response contains one or more ESI tags, these are processed in the same way. In other words, the gateway cache either retrieves the included page fragment from its cache or requests the page fragment from the backend application again. When all the ESI tags have been resolved, the gateway cache merges each into the main page and sends the final content to the client.

リクエストが処理されると、ゲートウェイ キャッシュはそのキャッシュからページ全体をフェッチするか、バックエンド アプリケーションからリクエストします。応答に 1 つ以上の ESI タグが含まれている場合、これらは同じ方法で処理されます。つまり、ゲートウェイ キャッシュは、キャッシュから含まれているページ フラグメントを取得するか、バックエンド アプリケーションからページ フラグメントを再度要求します。すべての ESI タグが解決されると、ゲートウェイ キャッシュはそれぞれをメインページにマージし、最終的なコンテンツをクライアントに送信します。

All of this happens transparently at the gateway cache level (i.e. outside of your application). As you'll see, if you choose to take advantage of ESI tags, Symfony makes the process of including them almost effortless.

これらはすべて、ゲートウェイ キャッシュ レベル (つまり、アプリケーションの外部) で透過的に行われます。ご覧のとおり、ESItags を利用することを選択した場合、Symfony はそれらを含めるプロセスをほとんど楽にします。

Using ESI in Symfony

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

まず、ESI を使用するには、アプリケーション構成で必ず有効にしてください。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
# config/packages/framework.yaml
framework:
    # ...
    esi: true

Now, suppose you have a page that is relatively static, except for a news ticker at the bottom of the content. With ESI, you can cache the news ticker independently of the rest of the page:

ここで、コンテンツの下部にある新しいステッカーを除いて、比較的静的なページがあるとします。 ESI を使用すると、ニュース ティッカーをページの残りの部分とは別にキャッシュできます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// src/Controller/DefaultController.php
namespace App\Controller;

// ...
class DefaultController extends AbstractController
{
    public function about(): Response
    {
        $response = $this->render('static/about.html.twig');
        $response->setPublic();
        $response->setMaxAge(600);

        return $response;
    }
}

In this example, the response is marked as public to make the full page cacheable for all requests with a lifetime of ten minutes. Next, include the news ticker in the template by embedding an action. This is done via the render() helper (for more details, see how to embed controllers in templates).

この例では、応答は公開としてマークされ、10 分間の有効期間を持つすべての要求に対して完全なページをキャッシュ可能にします。次に、アクションを埋め込むことによってテンプレートにニュース ティッカーを含めます。詳細については、テンプレートにコントローラーを埋め込む方法を参照してください)。

As the embedded content comes from another page (or controller for that matter), Symfony uses the standard render helper to configure ESI tags:

埋め込みコンテンツが別のページ (またはコントローラー) から取得されるため、Symfony は標準のレンダー ヘルパーを使用して ESI タグを構成します。
1
2
3
4
5
6
7
{# templates/static/about.html.twig #}

{# you can use a controller reference #}
{{ render_esi(controller('App\\Controller\\NewsController::latest', { 'maxPerPage': 5 })) }}

{# ... or a URL #}
{{ render_esi(url('latest_news', { 'maxPerPage': 5 })) }}

By using the esi renderer (via the render_esi() Twig function), you tell Symfony that the action should be rendered as an ESI tag. You might be wondering why you would want to use a helper instead of just writing the ESI tag yourself. That's because using a helper makes your application work even if there is no gateway cache installed.

esi レンダラーを (render_esi() Twig 関数を介して) 使用することで、アクションを ESI タグとしてレンダリングする必要があることを Symfony に伝えます。 ESItag を自分で記述するだけでなく、なぜヘルパーを使用したいのか、不思議に思うかもしれません。これは、ヘルパーを使用すると、ゲートウェイ キャッシュがインストールされていなくてもアプリケーションが機能するためです。

Tip

ヒント

As you'll see below, the maxPerPage variable you pass is available as an argument to your controller (i.e. $maxPerPage). The variables passed through render_esi also become part of the cache key so that you have unique caches for each combination of variables and values.

以下に示すように、渡す maxPerPage 変数は、コントローラーへの引数 (つまり、$maxPerPage) として使用できます。 render_esi を介して渡された変数もキャッシュ キーの一部になるため、変数と値の組み合わせごとに一意のキャッシュを作成できます。

When using the default render() function (or setting the renderer to inline), Symfony merges the included page content into the main one before sending the response to the client. But if you use the esi renderer (i.e. call render_esi()) and if Symfony detects that it's talking to a gateway cache that supports ESI, it generates an ESI include tag. But if there is no gateway cache or if it does not support ESI, Symfony will just merge the included page content within the main one as it would have done if you had used render().

デフォルトの render() 関数を使用する (またはレンダラーをインラインに設定する) 場合、Symfony はクライアントに応答を送信する前に、含まれているページ コンテンツをメインのコンテンツにマージします。しかし、esi レンダラーを使用する (つまり render_esi() を呼び出す) 場合、ESI をサポートするゲートウェイ キャッシュと通信していることを Symfony が検出すると、ESI インクルード タグが生成されます。しかし、ゲートウェイ キャッシュがない場合、または ESI をサポートしていない場合、symfony は、render() を使用した場合と同様に、含まれているページ コンテンツをメインのコンテンツにマージするだけです。

Note

ノート

Symfony considers that a gateway cache supports ESI if its request include the Surrogate-Capability HTTP header and the value of that header contains the ESI/1.0 string anywhere.

symfony は、リクエストに Surrogate-Capability HTTP ヘッダーが含まれ、そのヘッダーの値に ESI/1.0 文字列がどこかに含まれている場合、ゲートウェイキャッシュが ESI をサポートしていると見なします。

The embedded action can now specify its own caching rules entirely independently of the main page:

埋め込みアクションは、メイン ページとは完全に独立して独自のキャッシュ ルールを指定できるようになりました。
  • Attributes
    属性
  • PHP
    PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// src/Controller/NewsController.php
namespace App\Controller;

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

class NewsController extends AbstractController
{
    #[Cache(smaxage: 60)]
    public function latest(int $maxPerPage): Response
    {
        // ...
    }
}

In this example, the embedded action is cached publicly too because the contents are the same for all requests. However, in other cases you may need to make this response non-public and even non-cacheable, depending on your needs.

この例では、コンテンツがすべてのリクエストで同じであるため、埋め込まれたアクションもパブリックにキャッシュされます。ただし、必要に応じて、このレスポンスを非公開にしたり、キャッシュ不可にしたりする必要がある場合もあります。

Putting all the above code together, with ESI the full page cache will be valid for 600 seconds, but the news component cache will only last for 60 seconds.

上記のコードをすべてまとめると、ESI ではフル ページ キャッシュは 600 秒間有効ですが、ニュース コンポーネント キャッシュは 60 秒間しか持続しません。

When using a controller reference, the ESI tag should reference the embedded action as an accessible URL so the gateway cache can fetch it independently of the rest of the page. Symfony takes care of generating a unique URL for any controller reference and it is able to route them properly thanks to the FragmentListener that must be enabled in your configuration:

コントローラー参照を使用する場合、ESI タグは埋め込みアクションをアクセス可能な URL として参照する必要があります。これにより、ゲートウェイ キャッシュは、ページの残りの部分とは無関係にアクションを取得できます。 Symfony は、任意のコントローラー参照に対して一意の URL を生成し、構成で有効にする必要がある FragmentListener のおかげで適切にルーティングできます。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
# config/packages/framework.yaml
framework:
    # ...
    fragments: { path: /_fragment }

One great advantage of the ESI renderer is that you can make your application as dynamic as needed and at the same time, hit the application as little as possible.

ESI レンダラーの大きな利点の 1 つは、アプリケーションを必要に応じて動的にすると同時に、アプリケーションへの影響を最小限に抑えることができることです。

Caution

注意

The fragment listener only responds to signed requests. Requests are only signed when using the fragment renderer and the render_esi Twig function.

フラグメント リスナーは、署名付きのリクエストにのみ応答します。リクエストは、フラグメント レンダラーと render_esi Twigfunction を使用する場合にのみ署名されます。

The render_esi helper supports three other useful options:

render_esi ヘルパーは、他に 3 つの便利なオプションをサポートしています。
alt
Used as the alt attribute on the ESI tag, which allows you to specify an alternative URL to be used if the src cannot be found.
ESI タグの alt 属性として使用されます。これにより、src が見つからない場合に使用する代替 URL を指定できます。
ignore_errors
If set to true, an onerror attribute will be added to the ESI with a value of continue indicating that, in the event of a failure, the gateway cache will remove the ESI tag silently.
true に設定すると、onerror 属性が ESI に追加され、値 continue が指定され、障害が発生した場合にゲートウェイ キャッシュが ESI タグをサイレントに削除することを示します。
absolute_uri
If set to true, an absolute URI will be generated. default: false
true に設定すると、絶対 URI が生成されます。デフォルト: false

6.2

6.2

The absolute_uri option was introduced in Symfony 6.2.

absolute_uri オプションは Symfony 6.2 で導入されました。