Usare Varnish per accelerare il proprio sito

Poiché la cache di Symfony usa gli header standard della cache HTTP, Il reverse proxy di Symfony può essere facilmente sostituito da qualsiasi altro reverse proxy. Varnish è un acceleratore HTTP potente e open source, che è in grado di servire contenuti in cache in modo veloce e che include il supporto per Edge Side Include.

Reverse proxy fidati

Perché ESI funzioni correttamente e per usare gli header X-FORWARDED, occorre configurare Varnish come proxy fidato.

Rotte e header X-FORWARDED

Per essere sicuri che Symfony generi correttamente URL con Varnish, ci deve essere un header X-Forwarded-Port, in modo che Symfony usi il numero giusto di porta.

Questa porta dipende dalla configurazione. Ipotizziamo che le connessioni esterne vengano sulla porta HTTP predefinita, la 80. Per le connessioni HTTPS, c’è un altro proxy (non facendo Varnish HTTPS) sulla porta HTTPS predefinita, la 443, che gestisce SSL e gira le richieste come richieste HTTP a Varnish, con un header X-Forwarded-Proto. In questo case, si deve aggiungere la seguente parte di configurazione:

sub vcl_recv {
    if (req.http.X-Forwarded-Proto == "https" ) {
        set req.http.X-Forwarded-Port = "443";
    } else {
        set req.http.X-Forwarded-Port = "80";
    }
}

Nota

Ricordarsi di configurare framework.trusted_proxies nella configurazione di Symfony, in modo che Varnish sia visto come proxy fidato e gli header X-Forwarded-* usati.

Varnish gira automaticamente l’IP come X-Forwarded-For e lascia l’header X-Forwarded-Proto nella richiesta. Se non si configura Varnish come proxy fidato, Symfony vedrà tutte le richieste come provenienti tramite connessioni HTTP non sicure da Varnish, invece che dall’effettivo client.

Se non si imposta correttamente l’header X-Forwarded-Port, Symfony appenderà la porta in cui gira l’applicazione PHP, quando genererà URL assoluti, p.e. http://example.com:8080/my/path.

Assicurare comportamenti di cache coerenti

Varnish usa gli header di cache inviati dall’applicazione per determinare in che modo mettere in cache il contenuto. Tuttavia, le versioni precedenti a Varnish 4 non rispettavano Cache-Control: no-cache, no-store e private. Per assicurare un comportamento coerente, usare la seguente configurazione, se si usa ancora Varnish 3:

  • Varnish 3
    sub vcl_fetch {
        /* Varnish3 ignora Cache-Control: no-cache e private
           https://www.varnish-cache.org/docs/3.0/tutorial/increasing_your_hitrate.html#cache-control
         */
        if (beresp.http.Cache-Control ~ "private" ||
            beresp.http.Cache-Control ~ "no-cache" ||
            beresp.http.Cache-Control ~ "no-store"
        ) {
            return (hit_for_pass);
        }
    }
    

Suggerimento

Si può vedere il comportamento predefinito di Varnish in forma di file VCL: default.vcl per Varnish 3, builtin.vcl per Varnish 4.

Abilitare Edge Side Include (ESI)

Come spiegato nella sezione Edge Side Include, Symfony capisce se sta parlando o meno a un reverse proxy che capisca ESI. Quando si usa il reverse proxy di Symfony, non occorre fare nulla. Se invece si usa Varnish per risolvere i tag ESI, serve una ulteriore configurazione in Varnish. Symfony usa l’header Surrogate-Capability da Edge Architecture, descritto da Akamai.

Nota

Varnish supporta solo l’attributo src dei tag ESI (onerror e alt vengono ignorati).

Innanzitutto, configurare Varnish in modo che pubblicizzi il supporto ESI, aggiungendo un header Surrogate-Capability alle richieste rimandate all’applicazione di backend:

sub vcl_recv {
    // Aggiunge un header Surrogate-Capability per dichiarare il supporto a ESI.
    set req.http.Surrogate-Capability = "abc=ESI/1.0";
}

Nota

La parte abc dell’header non è importante, a meno non si abbiamo più surrogati che debbano pubblicizzare le loro capacità. Vedere Surrogate-Capability Header per dettagli.

Quindi, ottimizzare Varnish, in modo che analizzi solo il contenuto di risposte quando ci sia almeno un tag ESI, verificando l’header Surrogate-Control, aggiunto automaticamente da Symfony:

  • Varnish 4
    sub vcl_backend_response {
        // Verifica il riconoscimento di ESI e rimuove l'header Surrogate-Control
        if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
            unset beresp.http.Surrogate-Control;
            set beresp.do_esi = true;
        }
    }
    
  • Varnish 3
    sub vcl_fetch {
        // Verifica il riconoscimento di ESI e rimuove l'header Surrogate-Control
        if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
            unset beresp.http.Surrogate-Control;
            set beresp.do_esi = true;
        }
    }
    

Suggerimento

Per chi ha seguito il consiglio che assicura il comportamento coerente di cache, queste funzioni vcl esistono già. Basta aggiungere il codice all fine della funzione, non interferiranno a vicenda.

Invalidare la cache

Se si vuole mettere in cache un contenuto che cambia di frequente e servire comunque agli utenti la sua versione più recente, occorre invalidare tale contenuto. Anche se l’invalidazione della cache consente di purgare il contenuto dal proxy prima che scada, aggiunge complessità al sistema di cache.

Suggerimento

Il bundle FOSHttpCacheBundle si occupa di invalidazione di cache, aiutando a organizzare la strategia di cache e di invalidazione.

La documentazione di FOSHttpCacheBundle spiega come configurare Varnish e altri reverse proxy per l’invalidazione della cache.