-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Rewrite the varnish cookbook article #4627
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,27 +7,91 @@ How to Use Varnish to Speed up my Website | |
Because Symfony's cache uses the standard HTTP cache headers, the | ||
:ref:`symfony-gateway-cache` can easily be replaced with any other reverse | ||
proxy. `Varnish`_ is a powerful, open-source, HTTP accelerator capable of serving | ||
cached content quickly and including support for :ref:`Edge Side Includes <edge-side-includes>`. | ||
cached content fast and including support for :ref:`Edge Side Includes <edge-side-includes>`. | ||
|
||
Trusting Reverse Proxies | ||
------------------------ | ||
.. index:: | ||
single: Varnish; configuration | ||
|
||
Make Symfony Trust the Reverse Proxy | ||
------------------------------------ | ||
|
||
For ESI to work correctly and for the :ref:`X-FORWARDED <varnish-x-forwarded-headers>` | ||
headers to be used, you need to configure Varnish as a | ||
:doc:`trusted proxy </cookbook/request/load_balancer_reverse_proxy>`. | ||
|
||
.. index:: | ||
single: Varnish; configuration | ||
.. _varnish-x-forwarded-headers: | ||
|
||
Routing and X-FORWARDED Headers | ||
------------------------------- | ||
|
||
To ensure that the Symfony Router generates URLs correctly with Varnish, | ||
a ``X-Forwarded-Port`` header must be present for Symfony to use the | ||
correct port number. | ||
|
||
Configuration | ||
------------- | ||
This port depends on your setup. Lets say that external connections come in | ||
on the default HTTP port 80. For HTTPS connections, there is another proxy | ||
(as Varnish does not do HTTPS itself) on the default HTTPS port 443 that | ||
handles the SSL termination and forwards the requests as HTTP requests to | ||
Varnish with a ``X-Forwarded-Proto`` header. In this case, you need to add | ||
the following configuration snippet: | ||
|
||
As seen previously, Symfony is smart enough to detect whether it talks to a | ||
reverse proxy that understands ESI or not. It works out of the box when you | ||
use the Symfony reverse proxy, but you need a special configuration to make | ||
it work with Varnish. Thankfully, Symfony relies on yet another standard | ||
written by Akamai (`Edge Architecture`_), so the configuration tips in this | ||
chapter can be useful even if you don't use Symfony. | ||
.. code-block:: varnish4 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we only show varnish4? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we use varnish4 code highlighting and do not show tabs at all, because |
||
|
||
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"; | ||
} | ||
} | ||
|
||
.. note:: | ||
|
||
Remember to configure :ref:`framework.trusted_proxies <reference-framework-trusted-proxies>` | ||
in the Symfony configuration so that Varnish is seen as a trusted proxy | ||
and the ``X-Forwarded-*`` headers are used. | ||
|
||
Varnish automatically forwards the IP as ``X-Forwarded-For`` and leaves | ||
the ``X-Forwarded-Proto`` header in the request. If you do not configure | ||
Varnish as trusted proxy, Symfony will see all requests as coming through | ||
insecure HTTP connections from the Varnish host instead of the real client. | ||
|
||
If the ``X-Forwarded-Port`` header is not set correctly, Symfony will append | ||
the port where the PHP application is running when generating absolute URLs, | ||
e.g. ``http://example.com:8080/my/path``. | ||
|
||
Ensure Consistent Caching Behaviour | ||
----------------------------------- | ||
|
||
Varnish uses the cache headers sent by your application to determine how | ||
to cache content. However, versions prior to Varnish 4 did not respect | ||
``Cache-Control: no-cache``. To ensure consistent behaviour, use the following | ||
configuration if you are still using Varnish 3: | ||
|
||
.. configuration-block:: | ||
|
||
.. code-block:: varnish3 | ||
|
||
sub vcl_fetch { | ||
/* By default, Varnish3 ignores Cache-Control: no-cache and private | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i split up the cache handling and ESI as they have nothing to do with each other There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Additionally, we should recommend HTTP/1.1 (Cache-Control) over HTTP/1.0 (Pragma). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. did that. dropped the whole pragma topic. |
||
https://www.varnish-cache.org/docs/3.0/tutorial/increasing_your_hitrate.html#cache-control | ||
*/ | ||
if (beresp.http.Cache-Control ~ "no-cache" || | ||
beresp.http.Cache-Control ~ "private" | ||
) { | ||
return (hit_for_pass); | ||
} | ||
} | ||
|
||
Enable Edge Side Includes (ESI) | ||
------------------------------- | ||
|
||
As explained in the :ref:`Edge Side Includes section<edge-side-includes>`, | ||
Symfony detects whether it talks to a reverse proxy that understands ESI or | ||
not. When you use the Symfony reverse proxy, you don't need to do anything. | ||
But to make Varnish instead of Symfony resolve the ESI tags, you need some | ||
configuration in Varnish. Symfony uses the ``Surrogate-Capability`` header | ||
from the `Edge Architecture`_ described by Akamai. | ||
|
||
.. note:: | ||
|
||
|
@@ -58,22 +122,12 @@ Symfony adds automatically: | |
|
||
.. code-block:: varnish4 | ||
|
||
/* (https://www.varnish-cache.org/docs/4.0/whats-new/upgrading.html#req-not-available-in-vcl-backend-response) */ | ||
sub vcl_backend_response { | ||
// Check for ESI acknowledgement and remove Surrogate-Control header | ||
if (beresp.http.Surrogate-Control ~ "ESI/1.0") { | ||
unset beresp.http.Surrogate-Control; | ||
set beresp.do_esi = true; | ||
} | ||
/* By default Varnish ignores Pragma: nocache | ||
(https://www.varnish-cache.org/docs/4.0/users-guide/increasing-your-hitrate.html#cache-control) | ||
so in order avoid caching it has to be done explicitly */ | ||
if (beresp.http.Pragma ~ "no-cache") { | ||
// https://www.varnish-cache.org/docs/4.0/whats-new/upgrading.html#hit-for-pass-objects-are-created-using-beresp-uncacheable | ||
set beresp.uncacheable = true; | ||
set beresp.ttl = 120s; | ||
return (deliver); | ||
} | ||
} | ||
|
||
.. code-block:: varnish3 | ||
|
@@ -84,38 +138,13 @@ Symfony adds automatically: | |
unset beresp.http.Surrogate-Control; | ||
set beresp.do_esi = true; | ||
} | ||
/* By default Varnish ignores Cache-Control: nocache | ||
(https://www.varnish-cache.org/docs/3.0/tutorial/increasing_your_hitrate.html#cache-control), | ||
so in order avoid caching it has to be done explicitly */ | ||
if (beresp.http.Pragma ~ "no-cache" || | ||
beresp.http.Cache-Control ~ "no-cache" || | ||
beresp.http.Cache-Control ~ "private") { | ||
return (hit_for_pass); | ||
} | ||
} | ||
|
||
.. code-block:: varnish2 | ||
|
||
sub vcl_fetch { | ||
// Check for ESI acknowledgement and remove Surrogate-Control header | ||
if (beresp.http.Surrogate-Control ~ "ESI/1.0") { | ||
unset beresp.http.Surrogate-Control; | ||
esi; | ||
} | ||
/* By default Varnish ignores Cache-Control: nocache | ||
so in order avoid caching it has to be done explicitly */ | ||
if (beresp.http.Pragma ~ "no-cache" || | ||
beresp.http.Cache-Control ~ "no-cache" || | ||
beresp.http.Cache-Control ~ "private") { | ||
return (hit_for_pass); | ||
} | ||
} | ||
|
||
.. caution:: | ||
.. tip:: | ||
|
||
Compression with ESI was not supported in Varnish until version 3.0 | ||
(read `GZIP and Varnish`_). If you're not using Varnish 3.0, put a web | ||
server in front of Varnish to perform the compression. | ||
If you followed the advice about ensuring a consistent caching | ||
behaviour, those vcl functions already exist. Just append the code | ||
to the end of the function, they won't interfere with each other. | ||
|
||
.. index:: | ||
single: Varnish; Invalidation | ||
|
@@ -134,139 +163,8 @@ proxy before it has expired, it adds complexity to your caching setup. | |
invalidation by helping you to organize your caching and | ||
invalidation setup. | ||
|
||
Varnish can be configured to accept a special HTTP ``PURGE`` method | ||
that will invalidate the cache for a given resource: | ||
|
||
.. code-block:: varnish4 | ||
|
||
/* | ||
Connect to the backend server | ||
on the local machine on port 8080 | ||
*/ | ||
backend default { | ||
.host = "127.0.0.1"; | ||
.port = "8080"; | ||
} | ||
|
||
sub vcl_recv { | ||
/* | ||
Varnish default behavior doesn't support PURGE. | ||
Match the PURGE request and immediately do a cache lookup, | ||
otherwise Varnish will directly pipe the request to the backend | ||
and bypass the cache | ||
*/ | ||
if (req.request == "PURGE") { | ||
return(lookup); | ||
} | ||
} | ||
|
||
sub vcl_hit { | ||
// Match PURGE request | ||
if (req.request == "PURGE") { | ||
// Force object expiration for Varnish < 3.0 | ||
set obj.ttl = 0s; | ||
// Do an actual purge for Varnish >= 3.0 | ||
// purge; | ||
error 200 "Purged"; | ||
} | ||
} | ||
|
||
sub vcl_miss { | ||
/* | ||
Match the PURGE request and | ||
indicate the request wasn't stored in cache. | ||
*/ | ||
if (req.request == "PURGE") { | ||
error 404 "Not purged"; | ||
} | ||
} | ||
|
||
.. caution:: | ||
|
||
You must protect the ``PURGE`` HTTP method somehow to avoid random people | ||
purging your cached data. You can do this by setting up an access list: | ||
|
||
.. code-block:: varnish4 | ||
|
||
/* | ||
Connect to the backend server | ||
on the local machine on port 8080 | ||
*/ | ||
backend default { | ||
.host = "127.0.0.1"; | ||
.port = "8080"; | ||
} | ||
|
||
// ACL's can contain IP's, subnets and hostnames | ||
acl purge { | ||
"localhost"; | ||
"192.168.55.0"/24; | ||
} | ||
|
||
sub vcl_recv { | ||
// Match PURGE request to avoid cache bypassing | ||
if (req.request == "PURGE") { | ||
// Match client IP to the ACL | ||
if (!client.ip ~ purge) { | ||
// Deny access | ||
error 405 "Not allowed."; | ||
} | ||
// Perform a cache lookup | ||
return(lookup); | ||
} | ||
} | ||
|
||
sub vcl_hit { | ||
// Match PURGE request | ||
if (req.request == "PURGE") { | ||
// Force object expiration for Varnish < 3.0 | ||
set obj.ttl = 0s; | ||
// Do an actual purge for Varnish >= 3.0 | ||
// purge; | ||
error 200 "Purged"; | ||
} | ||
} | ||
|
||
sub vcl_miss { | ||
// Match PURGE request | ||
if (req.request == "PURGE") { | ||
// Indicate that the object isn't stored in cache | ||
error 404 "Not purged"; | ||
} | ||
} | ||
|
||
.. _varnish-x-forwarded-headers: | ||
|
||
Routing and X-FORWARDED Headers | ||
------------------------------- | ||
|
||
To ensure that the Symfony Router generates URLs correctly with Varnish, | ||
proper ```X-Forwarded``` headers must be added so that Symfony is aware of | ||
the original port number of the request. Exactly how this is done depends | ||
on your setup. As a simple example, Varnish and your web server are on the | ||
same machine and that Varnish is listening on one port (e.g. 80) and Apache | ||
on another (e.g. 8080). In this situation, Varnish should add the ``X-Forwarded-Port`` | ||
header so that the Symfony application knows that the original port number | ||
is 80 and not 8080. | ||
|
||
If this header weren't set properly, Symfony may append ``8080`` when generating | ||
absolute URLs: | ||
|
||
.. code-block:: varnish4 | ||
|
||
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"; | ||
} | ||
} | ||
|
||
.. note:: | ||
|
||
Remember to configure :ref:`framework.trusted_proxies <reference-framework-trusted-proxies>` | ||
in the Symfony configuration so that Varnish is seen as a trusted proxy | ||
and the ``X-Forwarded-`` headers are used. | ||
The documentation of the `FOSHttpCacheBundle`_ explains how to configure | ||
Varnish and other reverse proxies for cache invalidation. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it ok to just throw out the purge information here and reference FOSHttpCacheBundle instead? the x-forwarded-proto moved to the top as its actually always required to get a correct setup There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm fine with that. I would make FOSHttpCacheBundle a link here too though There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i did not link because its linked in the paragraph just above. but does |
||
|
||
.. _`Varnish`: https://www.varnish-cache.org | ||
.. _`Edge Architecture`: http://www.w3.org/TR/edge-arch | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
‘system’ is a bit vague: are you referring to an Apache or Nginx proxy here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes. or haproxy or some load balancer system that does the SSL termination. i don't really want to go into the details here as its out of scope, apart from the fact that the port has to be communicated. i am assuming that people doing this will know how to do it... do you have an idea how to make this better without digressing into server setup too much?