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 :)
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
Ü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). –