2013-07-16 14 views
5

Ich weiß nicht mehr, was ich mache. Ich hatte so viele Probleme, dass ich nicht weiß, wo ich anfangen soll. Hier ist meine Konfiguration:Symfony ESI bricht den Varnish-Cache, wenn Cookies aktiviert sind

varnishd (varnish-3.0.3 revision 9e6a70f) 
Server version: Apache/2.2.22 (Unix) 
Symfony 2.3.1 

Zuerst habe ich deaktiviert Symfony AppCache in der app.php-Datei, die als Reverse Proxy statt Lack verwendet wurde.

Hier ist meine Varnish Konfiguration:

Varnish (80) <--> Apache (8080)

# /etc/varnish/default.vcl 
backend default { 
    .host = "127.0.0.1"; 
    .port = "8080"; 
} 

sub vcl_recv { 
    if (req.http.Cache-Control ~ "no-cache") { 
     return (pass); 
    } 

    if (!(req.url ~ "^/dashboard/")) { 
     unset req.http.Cookie; 
    } 

    # Remove has_js and Google Analytics __* cookies. 
    set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_[_a-z]+|has_js)=[^;]*", ""); 
    # Remove a ";" prefix, if present. 
    set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", ""); 

    #set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(__[a-z]+|has_js)=[^;]*", ""); 
    set req.http.Surrogate-Capability = "abc=ESI/1.0"; 

    if (req.restarts == 0) { 
     if (req.http.x-forwarded-for) { 
      set req.http.X-Forwarded-For = 
      req.http.X-Forwarded-For + ", " + client.ip; 
     } else { 
      set req.http.X-Forwarded-For = client.ip; 
     } 
    } 

    if (req.request != "GET" && 
     req.request != "HEAD" && 
     req.request != "PUT" && 
     req.request != "POST" && 
     req.request != "TRACE" && 
     req.request != "OPTIONS" && 
     req.request != "DELETE") { 
     /* Non-RFC2616 or CONNECT which is weird. */ 
     return (pipe); 
    } 

f (req.request != "GET" && req.request != "HEAD") { 
     /* We only deal with GET and HEAD by default */ 
     return (pass); 
    } 

    if (req.http.Authorization) { 
     /* Not cacheable by default */ 
     return (pass); 
    } 

    return (lookup); 
} 

sub vcl_pipe { 
    return (pipe); 
} 

sub vcl_pass { 
    return (pass); 
} 

sub vcl_hash { 
    hash_data(req.url); 

    if (req.http.host) { 
     hash_data(req.http.host); 
    } else { 
     hash_data(server.ip); 
    } 

    return (hash); 
} 

sub vcl_fetch { 
    if (beresp.http.Surrogate-Control ~ "ESI/1.0") { 
     unset beresp.http.Surrogate-Control; 
     set beresp.do_esi = true; 
    } 

    # Varnish determined the object was not cacheable 
    if (beresp.ttl <= 0s) { 
     set beresp.http.X-Varnish-Cacheable = "NO:Not Cacheable"; 

    # You don't wish to cache content for logged in users 
    } elsif (req.http.Cookie ~ "(UserID|_session)") { 
     set beresp.http.X-Varnish-Cacheable = "NO:Got Session"; 
     return(hit_for_pass); 

    # You are respecting the Cache-Control=private header from the backend 
    } elsif (beresp.http.Cache-Control ~ "private") { 
     set beresp.http.X-Varnish-Cacheable = "NO:Cache-Control=private"; 
     return(hit_for_pass); 

    # Varnish determined the object was cacheable 
    } else { 
     set beresp.http.X-Varnish-Cacheable = "YES"; 
    } 

    if (beresp.status >= 300) { 
     return (hit_for_pass); 
    } 

    if (beresp.http.Pragma ~ "no-cache" || 
     beresp.http.Cache-Control ~ "no-cache" || 
     beresp.http.Cache-Control ~ "private") { 
     return (hit_for_pass); 
    } 

    return (deliver); 
} 

sub vcl_hit { 
    return (deliver); 
} 

sub vcl_deliver { 
    if (obj.hits > 0) { 
     set resp.http.X-Varnish-Cached = "HIT"; 
    } else { 
     set resp.http.X-Varnish-Cached = "MISS"; 
    } 
    return (deliver); 
} 

sub vcl_error { 
    set obj.http.Content-Type = "text/html; charset=utf-8"; 
    set obj.http.Retry-After = "5"; 

    synthetic {" 
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
<html> 
    <head> 
    <title>"} + obj.status + " " + obj.response + {"</title> 
    </head> 
    <body> 
    <h1>Error "} + obj.status + " " + obj.response + {"</h1> 
    <p>"} + obj.response + {"</p> 
    <h3>Guru Meditation:</h3> 
    <p>XID: "} + req.xid + {"</p> 
    <hr> 
    <p>Varnish cache server</p> 
    </body> 
</html> 
"}; 

    return (deliver); 
} 

sub vcl_init { 
    return (ok); 
} 

sub vcl_fini { 
    return (ok); 
} 

Wegen:

if (!(req.url ~ "^/dashboard/")) { 
    unset req.http.Cookie; 
} 

Lack entfernt alle Cookies der öffentlichen Seiten und trifft den Cache, was gut ist, aber ... Da Varnish alle Cookies löscht, kann ich nicht angemeldet bleiben (kein Session-Cookie bedeutet keine Sitzung).

Ich habe die Login-Schaltfläche in einem ESI-Block mit No-Cache-Header, aber das repariert es immer noch nicht. Hier sind die Frontend-Controller:

public function indexAction() 
{ 
    $response = $this->render('AcmeCoreBundle:Default:index.html.twig'); 
    $response->setSharedMaxAge(21600); // 6 hours 
    return $response; 
} 

Die Vorlage erstreckt sich die layout.html.twig:

<html lang="en-US"> 
<head> 
    ... 
</head> 
<body> 
    ... 

    <div class="top-right"> 
     {{ render_esi(controller('AcmeUserBundle:Default:loginBox')) }} 
    </div> 

    ... 
</body> 

und die Steuerung AcmeUserBundle:Default:loginBox:

public function loginBoxAction() 
{ 
    $response = $this->render('AcmeUserBundle:Block:home_login.html.twig'); 
    $response->setVary('Cookies', false); 
    $response->setMaxAge(0); 
    $response->setPrivate(); 

    return $response; 
} 

Ich weiß nicht, wie die Session-Cookie verwalten für Symfony. Da ich auf jeder Seite eine Facebook Connect-Schaltfläche habe, muss ich die Benutzersitzung haben. Und weil Symfony ein Cookie auch für anonyme Benutzer erstellt, habe ich Session-Cookies für alle Anfragen.

Hilfe würde sehr geschätzt :)

Danke, Maxime


UPDATE: Neue VCL-Dateien nach Empfehlungen:

... 

sub vcl_recv { 

    if (!(req.url ~ "^/dashboard") && !(req.url ~ "^/logout") && !(req.url ~ "^/_fragment") && req.esi_level == 0) { 
     set req.http.Esi-Cookie = req.http.Cookie; 
     unset req.http.Cookie; 
    } 

    if (!(req.url ~ "^/dashboard") && req.esi_level > 0) { 
     set req.http.Cookie = req.http.Esi-Cookie; 
    } 

    # Remove has_js and Google Analytics __* cookies. 
    set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_[_a-z]+|has_js)=[^;]*", ""); 

    # Remove a ";" prefix, if present. 
    set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", ""); 

    # Force ESI capability header 
    set req.http.Surrogate-Capability = "abc=ESI/1.0"; 

    if (req.restarts == 0) { 
     if (req.http.x-forwarded-for) { 
      set req.http.X-Forwarded-For = 
      req.http.X-Forwarded-For + ", " + client.ip; 
     } else { 
      set req.http.X-Forwarded-For = client.ip; 
     } 
    } 

    if (req.request != "GET" && 
     req.request != "HEAD" && 
     req.request != "PUT" && 
     req.request != "POST" && 
     req.request != "TRACE" && 
     req.request != "OPTIONS" && 
     req.request != "DELETE") { 
     /* Non-RFC2616 or CONNECT which is weird. */ 
     return (pipe); 
    } 

    # if Authorization or no-cache header we skip the cache 
    if (req.http.Authorization || req.http.Cache-Control ~ "no-cache") { 
     return (pass); 
    } 

    # If not GET or HEAD request we skip the cache 
    if (req.request != "GET" && req.request != "HEAD") { 
     return (pass); 
    } 

    return (lookup); 
} 

... 

sub vcl_fetch { 

    if (!(req.url ~ "^/dashboard") && !(req.url ~ "^/login_check")) { 
     unset beresp.http.set-cookie; 
    } 

    if (beresp.http.Surrogate-Control ~ "ESI/1.0") { 
     unset beresp.http.Surrogate-Control; 
     set beresp.do_esi = true; 
    } 

    # Varnish determined the object was not cacheable 
    if (beresp.ttl <= 0s) { 
     set beresp.http.X-Varnish-Cacheable = "NO:Not Cacheable"; 

    # You don't wish to cache content for logged in users 
    } elsif (req.http.Cookie ~ "(UserID|_session)") { 
     set beresp.http.X-Varnish-Cacheable = "NO:Got Session"; 
     return(hit_for_pass); 

    # You are respecting the Cache-Control=private header from the backend 
    } elsif (beresp.http.Cache-Control ~ "private") { 
     set beresp.http.X-Varnish-Cacheable = "NO:Cache-Control=private"; 
     return(hit_for_pass); 

    # Varnish determined the object was cacheable 
    } else { 
     set beresp.http.X-Varnish-Cacheable = "YES"; 
    } 

    if (beresp.status >= 300) { 
     return (hit_for_pass); 
    } 

    if (beresp.http.Pragma ~ "no-cache" || 
     beresp.http.Cache-Control ~ "no-cache" || 
     beresp.http.Cache-Control ~ "private") { 
     return (hit_for_pass); 
    } 

    return (deliver); 
} 

Jede öffentliche Seite wird nun im Cache gespeichert richtig . Ich habe Cache-HITs überall außer auf der Anmeldeseite, aber es ist keine große Sache für jetzt.

Das Problem, das ich habe, ist mit dem ESI-Block in der Kopfzeile. Ich kann in der Apache Zugriffsprotokoll sehen, dass Lack die <esi:include> ruft die URL anfordert.

Der auf der Homepage gerenderte ESI-Block ist nicht korrekt (er zeigt die Anmelde-URL an, als wäre der Benutzer nicht angemeldet), aber wenn ich direkt anrufe, ist der zurückgegebene Block korrekt (mit den Benutzerinformationen).

ich nicht wirklich wissen, woher es kommt, aber ich bin so nah :)

Antwort

5

die Cookie-Header aus einer eingehenden Anfrage entfernt sich aus allen Entfernen resultierenden ESI Anforderungen enthalten.Da Sie in enthaltenen Ressourcen Zugriff auf den Cookie-Header wollen, aber nicht die Eltern, die im Cache gespeicherten ist, versuchen Sie dies:

if (!(req.url ~ "^/dashboard/") && req.esi_level == 0) { 
    set req.http.Esi-Cookie = req.http.Cookie; 
    unset req.http.Cookie; 
} 
if (!(req.url ~ "^/dashboard/") && req.esi_level > 0) { 
    set req.http.Cookie = req.http.Esi-Cookie; 
} 

Dieser den Browser-Cookie von der Anforderung für die übergeordnete Seite Streifen, aber wieder fügt sie Esi-Anforderungen, die sich aus esi ergeben: Tags in die zurückgegebene Seite aufnehmen. Ich habe den obigen Code nicht gefunden, damit er nicht zu 100% perfekt ist.

if (!(req.url ~ "^/dashboard/") && req.esi_level > 0) { 
    set req.http.Cookie = req.http.Esi-Cookie; 
    return (pass); 
} 
+1

Dank Kumpel: Wenn Block

aktualisieren

In vcl_recv, wenn Sie überhaupt nicht gecached werden sollen oder esi rekursiv ein esi Fragment zu verarbeiten, können Sie die zweite ändern. Da Symfony auch für anonyme Benutzer ein Sitzungscookie erstellt, könnte die Erstellung eines zweiten Cookies für angemeldete Benutzer die Lösung sein? Wenn 'logged_in' cookie present den Cookie nicht löscht, löscht er sonst die Cookies. Dies würde Cache MISS für eingeloggte Benutzer und Cache HIT für anonyme Benutzer zur Folge haben. Was denken Sie? – maxwell2022

+1

Überprüfen Sie die Cookie-Kopfzeile der Anforderungen, die an den Lack gestellt werden, um sicherzustellen, dass Ihre VCL ihn richtig einstellt. Wenn Sie die Ressource/_fragment direkt anstoßen, überprüfen Sie auch die Antwortheader, um sicherzustellen, dass sie explizit nicht zwischengespeichert werden (oder zurückgeben (übergeben) für die esi-Anforderungen, wie ich es im Update vorschlage). –

Verwandte Themen