<?xml version="1.0" encoding="utf-8" ?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:syn="http://purl.org/rss/1.0/modules/syndication/" xmlns="http://purl.org/rss/1.0/">




    



<channel rdf:about="http://www.nedproductions.biz/wiki/front-page/RSS">
  <title>FrontPage</title>
  <link>http://www.nedproductions.biz</link>

  <description>
    
      
    
  </description>

  

  
            <syn:updatePeriod>daily</syn:updatePeriod>
            <syn:updateFrequency>1</syn:updateFrequency>
            <syn:updateBase>2010-01-20T11:56:14Z</syn:updateBase>
        

  <image rdf:resource="http://www.nedproductions.biz/logo.png"/>

  <items>
    <rdf:Seq>
      
        <rdf:li rdf:resource="http://www.nedproductions.biz/wiki/a-perfected-varnish-reverse-caching-proxy-vcl-script"/>
      
      
        <rdf:li rdf:resource="http://www.nedproductions.biz/wiki/configuring-a-proxmox-ve-2.x-cluster-running-over-an-openvpn-intranet"/>
      
      
        <rdf:li rdf:resource="http://www.nedproductions.biz/wiki/replacing-plones-openid-login-with-an-openid-selector-for-google-yahoo-etc"/>
      
      
        <rdf:li rdf:resource="http://www.nedproductions.biz/wiki/setting-up-a-content-caching-server"/>
      
      
        <rdf:li rdf:resource="http://www.nedproductions.biz/wiki/setting-up-a-geo-targeting-domain-name-server"/>
      
    </rdf:Seq>
  </items>

</channel>


  <item rdf:about="http://www.nedproductions.biz/wiki/a-perfected-varnish-reverse-caching-proxy-vcl-script">
    <title>A perfected varnish reverse caching proxy VCL script</title>
    <link>http://www.nedproductions.biz/wiki/a-perfected-varnish-reverse-caching-proxy-vcl-script</link>
    <description>A useful template default.vcl for others to base theirs upon.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>It's painful getting your varnish default.vcl perfectly correct, so here's one to get you started. <b>Requires</b> varnish v2.0.5 or later, if you use this with a version before that then be aware that a If-None-Match in the headers will always default in older varnishs to being processed as pass (i.e. bypass the cache) which you may not want to happen. Features of the following VCL:</p>
<ol>
<li>Designed to primarily cache Zope and <a class="external-link" href="http://www.plone.org" target="_blank">Plone</a> content.</li>
<li>Gives an example of how to "mount" parts of other websites onto your own e.g. here I mount Google Finance and the RePeC economics database. VERY useful for AJAX programming!</li>
<li>Uses cunningly designed statically compiled regular expressions to perform VirtualHostMonster style auto-generation of VirtualHostBase paths out of the HTTP headers (includes HTTPS support!). This saves you having to specify them manually for each and every site you host.</li>
<li>Enables varnish to serve stale content up to 24h old if Zope has gone down (very useful for performing upgrades), but keeps grace at 20s if the Zope backend is healthy. Note that this bit particularly requires the If-None-Match ETag support in varnish v2.0.5 and later.</li>
<li>Patches SVGZ files to be returned as a more compatible SVG + gzip content encoded.</li>
<li>Harmonises Content-Encoding to avoid caching too many copies.</li>
</ol>
<pre># This VCL config file is adapted from template.vcl in http://pypi.python.org/pypi/plone.recipe.varnish<br />backend default {<br />    .host = "localhost";<br />    .port = "6100";<br />    .first_byte_timeout = 300s; /* varnish v2.0.3 or later only */<br />    .probe = {<br />        .url = "/";<br />        .timeout = 1s;<br />        .interval = 5s;<br />        .window = 1;<br />        .threshold = 1;<br />    }<br />}<br /><br />backend repec {<br />        .host = "ideas.repec.org";<br />        .port = "80";<br />}<br /><br />backend google {<br />    .host = "209.85.229.106"; /*www.google.com";*/<br />    .port = "80";<br />}<br /><br />/* Only permit cluster to purge files from cache */<br />acl purge {<br />    "dedi1.nedprod.com";<br />    "europe1.nedproductions.biz";<br />    "usa1.nedproductions.biz";<br />    "localhost";<br />}<br /><br />sub vcl_recv {<br />    /* Before anything else we need to fix gzip compression */<br />    if (req.http.Accept-Encoding) {<br />        if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {<br />            # No point in compressing these<br />            remove req.http.Accept-Encoding;<br />        } else if (req.http.Accept-Encoding ~ "gzip") {<br />            set req.http.Accept-Encoding = "gzip";<br />        } else if (req.http.Accept-Encoding ~ "deflate") {<br />            set req.http.Accept-Encoding = "deflate";<br />        } else {<br />            # unknown algorithm<br />            remove req.http.Accept-Encoding;<br />        }<br />    }<br /><br />    if (req.request == "PURGE") {<br />        if (!client.ip ~ purge) {<br />            error 405 "Not allowed.";<br />        }<br />        /* Always purge by URL rather than going via vcl_hash<br />           as it hashes other factors which break purging */<br />        purge_url(req.url);<br />        error 200 "Purged";<br />    }<br /><br />    /* Rewrite all requests to /repec/cgi-bin/authorref.cgi to http://ideas.repec.org/cgi-bin/authorref.cgi */<br />    if (req.url ~ "^/repec/cgi-bin/authorref.cgi\?handle=" || req.url ~ "^/repec/cgi-bin/ref.cgi\?handle=") {<br />        set req.http.host = "ideas.repec.org";<br />        set req.url = regsub(req.url, "^/repec", "");<br />        set req.backend = repec;<br />        remove req.http.Cookie;<br />        lookup;<br />    } else if(req.url ~ "^/googlefinance/finance/converter\?") {<br />        set req.http.host = "www.google.com";<br />        set req.url = regsub(req.url, "^/googlefinance", "");<br />        set req.backend = google;<br />        remove req.http.Cookie;<br />        lookup;<br />    } else {<br />        set req.backend = default;<br />        if (req.http.X-Forwarded-Proto == "https" ) {<br />            set req.http.X-Forwarded-Port = "443";<br />        } else {<br />            set req.http.X-Forwarded-Port = "80";<br />        }<br />        if (req.http.host ~ "^(www\.|ipv6\.|usa1\.|europe1\.)?([-0-9a-zA-Z]+)\.([a-zA-Z]+)$") {<br />            set req.http.host = regsub(req.http.host, "^(www\.|ipv6\.|usa1\.|europe1\.)?([-0-9a-zA-Z]+)\.([a-zA-Z]+)$", "\1\2.\3");<br />            set req.url = "/VirtualHostBase/" req.http.X-Forwarded-Proto<br />                regsub(req.http.host, "^(www\.|ipv6\.|usa1\.|europe1\.)?([-0-9a-zA-Z]+)\.([a-zA-Z]+)$", "/\1\2.\3:")<br />                req.http.X-Forwarded-Port<br />                regsub(req.http.host, "^(www\.|ipv6\.|usa1\.|europe1\.)?([-0-9a-zA-Z]+)\.([a-zA-Z]+)$", "/\2.\3/\2.\3/VirtualHostRoot")<br />                req.url;<br />        }<br />    }<br /><br /><br />    if (req.request != "GET" &amp;&amp;<br />        req.request != "HEAD" &amp;&amp;<br />        req.request != "PUT" &amp;&amp;<br />        req.request != "POST" &amp;&amp;<br />        req.request != "TRACE" &amp;&amp;<br />        req.request != "OPTIONS" &amp;&amp;<br />        req.request != "DELETE") {<br />        /* Non-RFC2616 or CONNECT which is weird. */<br />        pipe;<br />    }<br /><br />    if (req.request != "GET" &amp;&amp; req.request != "HEAD") {<br />        /* We only deal with GET and HEAD by default */<br />        pass;<br />    }<br /><br />    if (req.http.Cookie) {<br />            # We only care about the "__ac.*" cookies, used for authentication and special persistent p_* cookies.<br />            if (req.http.Cookie ~ "__ac.*" ) {<br />                    pass;<br />        }<br />        # Else strip all cookies<br />        remove req.http.Cookie;<br />        }<br /><br />    if (req.url ~ "createObject") {<br />        pass;<br />    }<br /><br />    /* Keep serving if haproxy goes down (ie; Plone is being serviced). Haproxy<br />    will return a 503 error page if Plone goes down for us */<br />    if (req.backend.healthy) {<br />        set req.grace = 20s; /* Only enable if you don't mind slightly stale content */<br />    } else {<br />        set req.grace = 24h;<br />    }<br /><br />    lookup;<br />}<br /><br />sub vcl_pipe {<br />    # This is not necessary if you do not do any request rewriting.<br />    set req.http.connection = "close";<br />}<br /><br />sub vcl_hash {<br />    # Normally it hashes on URL and Host but we rewrite the host<br />    # into a VirtualHostBase URL. Therefore we can hash on URL alone.<br />    set req.hash += req.url;<br /><br />    # One needs to include compression state normalised above<br />    if (req.http.Accept-Encoding) {<br />        set req.hash += req.http.Accept-Encoding;<br />    }<br /><br />    # Differentiate based on login cookie too<br />    #set req.hash += req.http.cookie;<br /><br />    return (hash);<br />}<br /><br />sub vcl_hit {<br />    if (req.request == "PURGE") {<br />        purge_url(req.url);<br />        error 200 "Purged";<br />    }<br />}<br /><br />sub vcl_miss {<br />    if (req.request == "PURGE") {<br />        error 404 "Not in cache";<br />    }<br />}<br /><br />sub vcl_fetch {<br />    set obj.grace = 24h; /* Keep at longest used in vcl_recv */<br /><br />    if (req.url ~ "\.svgz$") {<br />        # Add a Content-Encoding to match compressed SVG<br />        set obj.http.Content-Type = "image/svg+xml";<br />        set obj.http.Content-Encoding = "gzip";<br />        remove obj.http.Content-Length;<br />        set obj.ttl = 86400s;<br />        set obj.http.Cache-Control = "max-age=3600";<br />        deliver;<br />    }<br />    if (req.http.host == "ideas.repec.org" || req.http.host == "www.google.com") {<br />        set obj.http.Content-Type = "text/html; charset=utf-8"; /* Correct the wrong response */<br />        set obj.ttl = 86400s;<br />        set obj.http.Cache-Control = "max-age=3600";<br />        deliver;<br />    }<br />    if (obj.http.Set-Cookie) {<br />        pass;<br />    }<br />    if (req.http.Authorization &amp;&amp; !obj.http.Cache-Control ~ "public") {<br />        pass;<br />    }<br />    /* Only use this if you wish to override Plone's CacheFu */<br />    if (obj.ttl &lt; 3600s) {<br />        if (obj.http.Cache-Control ~ "(private|no-cache|no-store)") {<br />            set obj.ttl = 60s; /* Caching everything anonymous for 60s is handy for being slashdotted :) */<br />        } else {<br />            set obj.ttl = 3600s;<br />        }<br />    }<br />}<br /><br /><br />sub vcl_error {<br />    set obj.http.Content-Type = "text/html; charset=utf-8";<br />    synthetic {"<br />&lt;?xml version="1.0" encoding="utf-8"?&gt;<br />&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"<br /> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;<br />&lt;html&gt;<br />  &lt;head&gt;<br />    &lt;title&gt;"} obj.status " " obj.response {"&lt;/title&gt;<br />  &lt;/head&gt;<br />  &lt;body&gt;<br />    &lt;div style="background-color:yellow;"&gt;<br />      &lt;h1&gt;This website is unavailable&lt;/h1&gt;<br />      &lt;p&gt;If you are seeing this page, either maintenance is being performed<br />      or something really bad has happened. Try returning in a few minutes.&lt;/p&gt;<br />      &lt;h2&gt;Error "} obj.status " " obj.response {"&lt;/h2&gt;<br />      &lt;p&gt;"} obj.response {"&lt;/p&gt;<br />      &lt;h3&gt;Guru Meditation:&lt;/h3&gt;<br />      &lt;p&gt;XID: "} req.xid {"&lt;/p&gt;<br />      &lt;address&gt;<br />         &lt;a href="http://www.nedproductions.biz/"&gt;ned Productions Ltd.&lt;/a&gt;<br />      &lt;/address&gt;<br />    &lt;/div&gt;<br />    &lt;div style="position:fixed;top:0;left:0;width:100%;height:100%;z-index:-1;"&gt;<br />    &lt;img alt="" src="/static/BBCTestCard.jpg" style="width:100%;height:100%" /&gt;&lt;/div&gt;<br />  &lt;/body&gt;<br />&lt;/html&gt;<br />"};<br />    return (deliver);<br />}<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />#Below is a commented-out copy of the default VCL logic.  If you<br />#redefine any of these subroutines, the built-in logic will be<br />#appended to your code.<br />#<br />#sub vcl_recv {<br />#    if (req.request != "GET" &amp;&amp;<br />#      req.request != "HEAD" &amp;&amp;<br />#      req.request != "PUT" &amp;&amp;<br />#      req.request != "POST" &amp;&amp;<br />#      req.request != "TRACE" &amp;&amp;<br />#      req.request != "OPTIONS" &amp;&amp;<br />#      req.request != "DELETE") {<br />#        /* Non-RFC2616 or CONNECT which is weird. */<br />#        return (pipe);<br />#    }<br />#    if (req.request != "GET" &amp;&amp; req.request != "HEAD") {<br />#        /* We only deal with GET and HEAD by default */<br />#        return (pass);<br />#    }<br />#    if (req.http.Authorization || req.http.Cookie) {<br />#        /* Not cacheable by default */<br />#        return (pass);<br />#    }<br />#    return (lookup);<br />#}<br />#<br />#sub vcl_pipe {<br />#    return (pipe);<br />#}<br />#<br />#sub vcl_pass {<br />#    return (pass);<br />#}<br />#<br />#sub vcl_hash {<br />#    set req.hash += req.url;<br />#    if (req.http.host) {<br />#        set req.hash += req.http.host;<br />#    } else {<br />#        set req.hash += server.ip;<br />#    }<br />#    return (hash);<br />#}<br />#<br />#sub vcl_hit {<br />#    if (!obj.cacheable) {<br />#        return (pass);<br />#    }<br />#    return (deliver);<br />#}<br />#<br />#sub vcl_miss {<br />#    return (fetch);<br />#}<br />#<br />#sub vcl_fetch {<br />#    if (!obj.cacheable) {<br />#        return (pass);<br />#    }<br />#    if (obj.http.Set-Cookie) {<br />#        return (pass);<br />#    }<br />#    set obj.prefetch =  -30s;<br />#    return (deliver);<br />#}<br />#<br />#sub vcl_deliver {<br />#    return (deliver);<br />#}<br />#<br />#sub vcl_discard {<br />#    /* XXX: Do not redefine vcl_discard{}, it is not yet supported */<br />#    return (discard);<br />#}<br />#<br />#sub vcl_prefetch {<br />#    /* XXX: Do not redefine vcl_prefetch{}, it is not yet supported */<br />#    return (fetch);<br />#}<br />#<br />#sub vcl_timeout {<br />#    /* XXX: Do not redefine vcl_timeout{}, it is not yet supported */<br />#    return (discard);<br />#}<br />#<br />#sub vcl_error {<br />#    set obj.http.Content-Type = "text/html; charset=utf-8";<br />#    synthetic {"<br />#&lt;?xml version="1.0" encoding="utf-8"?&gt;<br />#&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"<br /># "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;<br />#&lt;html&gt;<br />#  &lt;head&gt;<br />#    &lt;title&gt;"} obj.status " " obj.response {"&lt;/title&gt;<br />#  &lt;/head&gt;<br />#  &lt;body&gt;<br />#    &lt;h1&gt;Error "} obj.status " " obj.response {"&lt;/h1&gt;<br />#    &lt;p&gt;"} obj.response {"&lt;/p&gt;<br />#    &lt;h3&gt;Guru Meditation:&lt;/h3&gt;<br />#    &lt;p&gt;XID: "} req.xid {"&lt;/p&gt;<br />#    &lt;address&gt;<br />#       &lt;a href="http://www.varnish-cache.org/"&gt;Varnish&lt;/a&gt;<br />#    &lt;/address&gt;<br />#  &lt;/body&gt;<br />#&lt;/html&gt;<br />#"};<br />#    return (deliver);<br />#}<br /></pre>
<div id="_mcePaste" style="position: absolute; width: 1px; height: 1px;"># This VCL config file is adapted from template.vcl in http://pypi.python.org/pypi/plone.recipe.varnish<br />backend default {<br /> .host = "localhost";<br /> .port = "6100";<br /> .first_byte_timeout = 300s; /* varnish v2.0.3 or later only */<br /> .probe = {<br /> .url = "/";<br /> .timeout = 1s;<br /> .interval = 5s;<br /> .window = 1;<br /> .threshold = 1;<br /> }<br />}<br /><br />backend repec {<br /> .host = "ideas.repec.org";<br /> .port = "80";<br />}<br /><br />backend google {<br /> .host = "209.85.229.106"; /*www.google.com";*/<br /> .port = "80";<br />}<br /><br />/* Only permit cluster to purge files from cache */<br />acl purge {<br /> "dedi1.nedprod.com";<br /> "europe1.nedproductions.biz";<br /> "usa1.nedproductions.biz";<br /> "localhost";<br />}<br /><br />sub vcl_recv {<br /> /* Before anything else we need to fix gzip compression */<br /> if (req.http.Accept-Encoding) {<br /> if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {<br /> # No point in compressing these<br /> remove req.http.Accept-Encoding;<br /> } else if (req.http.Accept-Encoding ~ "gzip") {<br /> set req.http.Accept-Encoding = "gzip";<br /> } else if (req.http.Accept-Encoding ~ "deflate") {<br /> set req.http.Accept-Encoding = "deflate";<br /> } else {<br /> # unknown algorithm<br /> remove req.http.Accept-Encoding;<br /> }<br /> }<br /><br /> if (req.request == "PURGE") {<br /> if (!client.ip ~ purge) {<br /> error 405 "Not allowed.";<br /> }<br /> /* Always purge by URL rather than going via vcl_hash<br /> as it hashes other factors which break purging */<br /> purge_url(req.url);<br /> error 200 "Purged";<br /> }<br /><br /> /* Rewrite all requests to /repec/cgi-bin/authorref.cgi to http://ideas.repec.org/cgi-bin/authorref.cgi */<br /> if (req.url ~ "^/repec/cgi-bin/authorref.cgi\?handle=" || req.url ~ "^/repec/cgi-bin/ref.cgi\?handle=") {<br /> set req.http.host = "ideas.repec.org";<br /> set req.url = regsub(req.url, "^/repec", "");<br /> set req.backend = repec;<br /> remove req.http.Cookie;<br /> lookup;<br /> } else if(req.url ~ "^/googlefinance/finance/converter\?") {<br /> set req.http.host = "www.google.com";<br /> set req.url = regsub(req.url, "^/googlefinance", "");<br /> set req.backend = google;<br /> remove req.http.Cookie;<br /> lookup;<br /> } else {<br /> set req.backend = default;<br /> if (req.http.X-Forwarded-Proto == "https" ) {<br /> set req.http.X-Forwarded-Port = "443";<br /> } else {<br /> set req.http.X-Forwarded-Port = "80";<br /> }<br /> if (req.http.host ~ "^(www\.|ipv6\.|usa1\.|europe1\.)?([-0-9a-zA-Z]+)\.([a-zA-Z]+)$") {<br /> set req.http.host = regsub(req.http.host, "^(www\.|ipv6\.|usa1\.|europe1\.)?([-0-9a-zA-Z]+)\.([a-zA-Z]+)$", "\1\2.\3");<br /> set req.url = "/VirtualHostBase/" req.http.X-Forwarded-Proto<br /> regsub(req.http.host, "^(www\.|ipv6\.|usa1\.|europe1\.)?([-0-9a-zA-Z]+)\.([a-zA-Z]+)$", "/\1\2.\3:")<br /> req.http.X-Forwarded-Port<br /> regsub(req.http.host, "^(www\.|ipv6\.|usa1\.|europe1\.)?([-0-9a-zA-Z]+)\.([a-zA-Z]+)$", "/\2.\3/\2.\3/VirtualHostRoot")<br /> req.url;<br /> }<br /> }<br /><br /><br /> if (req.request != "GET" &amp;&amp;<br /> req.request != "HEAD" &amp;&amp;<br /> req.request != "PUT" &amp;&amp;<br /> req.request != "POST" &amp;&amp;<br /> req.request != "TRACE" &amp;&amp;<br /> req.request != "OPTIONS" &amp;&amp;<br /> req.request != "DELETE") {<br /> /* Non-RFC2616 or CONNECT which is weird. */<br /> pipe;<br /> }<br /><br /> if (req.request != "GET" &amp;&amp; req.request != "HEAD") {<br /> /* We only deal with GET and HEAD by default */<br /> pass;<br /> }<br /><br /> if (req.http.Cookie) {<br /> # We only care about the "__ac.*" cookies, used for authentication and special persistent p_* cookies.<br /> if (req.http.Cookie ~ "__ac.*" ) {<br /> pass;<br /> }<br /> # Else strip all cookies<br /> remove req.http.Cookie;<br /> }<br /><br /> if (req.url ~ "createObject") {<br /> pass;<br /> }<br /><br /> /* Keep serving if haproxy goes down (ie; Plone is being serviced). Haproxy<br /> will return a 503 error page if Plone goes down for us */<br /> if (req.backend.healthy) {<br /> set req.grace = 20s; /* Only enable if you don't mind slightly stale content */<br /> } else {<br /> set req.grace = 24h;<br /> }<br /><br /> lookup;<br />}<br /><br />sub vcl_pipe {<br /> # This is not necessary if you do not do any request rewriting.<br /> set req.http.connection = "close";<br />}<br /><br />sub vcl_hash {<br /> # Normally it hashes on URL and Host but we rewrite the host<br /> # into a VirtualHostBase URL. Therefore we can hash on URL alone.<br /> set req.hash += req.url;<br /><br /> # One needs to include compression state normalised above<br /> if (req.http.Accept-Encoding) {<br /> set req.hash += req.http.Accept-Encoding;<br /> }<br /><br /> # Differentiate based on login cookie too<br /> #set req.hash += req.http.cookie;<br /><br /> return (hash);<br />}<br /><br />sub vcl_hit {<br /> if (req.request == "PURGE") {<br /> purge_url(req.url);<br /> error 200 "Purged";<br /> }<br />}<br /><br />sub vcl_miss {<br /> if (req.request == "PURGE") {<br /> error 404 "Not in cache";<br /> }<br />}<br /><br />sub vcl_fetch {<br /> set obj.grace = 24h; /* Keep at longest used in vcl_recv */<br /><br /> /* Manual If-None-Match support */<br /> /*if (obj.http.etag == req.http.if-none-match) {<br /> error 304 "Not Modified";<br /> }*/<br /><br /> if (req.url ~ "\.svgz$") {<br /> # Add a Content-Encoding to match compressed SVG<br /> set obj.http.Content-Type = "image/svg+xml";<br /> set obj.http.Content-Encoding = "gzip";<br /> remove obj.http.Content-Length;<br /> set obj.ttl = 86400s;<br /> set obj.http.Cache-Control = "max-age=3600";<br /> deliver;<br /> }<br /> if (req.http.host == "ideas.repec.org" || req.http.host == "www.google.com") {<br /> set obj.http.Content-Type = "text/html; charset=utf-8"; /* Correct the wrong response */<br /> set obj.ttl = 86400s;<br /> set obj.http.Cache-Control = "max-age=3600";<br /> deliver;<br /> }<br /> if (obj.http.Set-Cookie) {<br /> pass;<br /> }<br /> if (req.http.Authorization &amp;&amp; !obj.http.Cache-Control ~ "public") {<br /> pass;<br /> }<br /> /* Only use this if you wish to override Plone's CacheFu */<br /> if (obj.ttl &lt; 3600s) {<br /> if (obj.http.Cache-Control ~ "(private|no-cache|no-store)") {<br /> set obj.ttl = 60s; /* Caching everything anonymous for 60s is handy for being slashdotted :) */<br /> } else {<br /> set obj.ttl = 3600s;<br /> }<br /> }<br />}<br /><br /><br />sub vcl_error {<br /> set obj.http.Content-Type = "text/html; charset=utf-8";<br /> synthetic {"<br />&lt;?xml version="1.0" encoding="utf-8"?&gt;<br />&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"<br /> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;<br />&lt;html&gt;<br /> &lt;head&gt;<br /> &lt;title&gt;"} obj.status " " obj.response {"&lt;/title&gt;<br /> &lt;/head&gt;<br /> &lt;body&gt;<br /> &lt;div style="background-color:yellow;"&gt;<br /> &lt;h1&gt;This website is unavailable&lt;/h1&gt;<br /> &lt;p&gt;If you are seeing this page, either maintenance is being performed<br /> or something really bad has happened. Try returning in a few minutes.&lt;/p&gt;<br /> &lt;h2&gt;Error "} obj.status " " obj.response {"&lt;/h2&gt;<br /> &lt;p&gt;"} obj.response {"&lt;/p&gt;<br /> &lt;h3&gt;Guru Meditation:&lt;/h3&gt;<br /> &lt;p&gt;XID: "} req.xid {"&lt;/p&gt;<br /> &lt;address&gt;<br /> &lt;a href="http://www.nedproductions.biz/"&gt;ned Productions Ltd.&lt;/a&gt;<br /> &lt;/address&gt;<br /> &lt;/div&gt;<br /> &lt;div style="position:fixed;top:0;left:0;width:100%;height:100%;z-index:-1;"&gt;<br /> &lt;img alt="" src="/static/BBCTestCard.jpg" style="width:100%;height:100%" /&gt;&lt;/div&gt;<br /> &lt;/body&gt;<br />&lt;/html&gt;<br />"};<br /> return (deliver);<br />}<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />#Below is a commented-out copy of the default VCL logic.  If you<br />#redefine any of these subroutines, the built-in logic will be<br />#appended to your code.<br />#<br />#sub vcl_recv {<br />#    if (req.request != "GET" &amp;&amp;<br />#      req.request != "HEAD" &amp;&amp;<br />#      req.request != "PUT" &amp;&amp;<br />#      req.request != "POST" &amp;&amp;<br />#      req.request != "TRACE" &amp;&amp;<br />#      req.request != "OPTIONS" &amp;&amp;<br />#      req.request != "DELETE") {<br />#        /* Non-RFC2616 or CONNECT which is weird. */<br />#        return (pipe);<br />#    }<br />#    if (req.request != "GET" &amp;&amp; req.request != "HEAD") {<br />#        /* We only deal with GET and HEAD by default */<br />#        return (pass);<br />#    }<br />#    if (req.http.Authorization || req.http.Cookie) {<br />#        /* Not cacheable by default */<br />#        return (pass);<br />#    }<br />#    return (lookup);<br />#}<br />#<br />#sub vcl_pipe {<br />#    return (pipe);<br />#}<br />#<br />#sub vcl_pass {<br />#    return (pass);<br />#}<br />#<br />#sub vcl_hash {<br />#    set req.hash += req.url;<br />#    if (req.http.host) {<br />#        set req.hash += req.http.host;<br />#    } else {<br />#        set req.hash += server.ip;<br />#    }<br />#    return (hash);<br />#}<br />#<br />#sub vcl_hit {<br />#    if (!obj.cacheable) {<br />#        return (pass);<br />#    }<br />#    return (deliver);<br />#}<br />#<br />#sub vcl_miss {<br />#    return (fetch);<br />#}<br />#<br />#sub vcl_fetch {<br />#    if (!obj.cacheable) {<br />#        return (pass);<br />#    }<br />#    if (obj.http.Set-Cookie) {<br />#        return (pass);<br />#    }<br />#    set obj.prefetch =  -30s;<br />#    return (deliver);<br />#}<br />#<br />#sub vcl_deliver {<br />#    return (deliver);<br />#}<br />#<br />#sub vcl_discard {<br />#    /* XXX: Do not redefine vcl_discard{}, it is not yet supported */<br />#    return (discard);<br />#}<br />#<br />#sub vcl_prefetch {<br />#    /* XXX: Do not redefine vcl_prefetch{}, it is not yet supported */<br />#    return (fetch);<br />#}<br />#<br />#sub vcl_timeout {<br />#    /* XXX: Do not redefine vcl_timeout{}, it is not yet supported */<br />#    return (discard);<br />#}<br />#<br />#sub vcl_error {<br />#    set obj.http.Content-Type = "text/html; charset=utf-8";<br />#    synthetic {"<br />#&lt;?xml version="1.0" encoding="utf-8"?&gt;<br />#&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"<br /># "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;<br />#&lt;html&gt;<br />#  &lt;head&gt;<br />#    &lt;title&gt;"} obj.status " " obj.response {"&lt;/title&gt;<br />#  &lt;/head&gt;<br />#  &lt;body&gt;<br />#    &lt;h1&gt;Error "} obj.status " " obj.response {"&lt;/h1&gt;<br />#    &lt;p&gt;"} obj.response {"&lt;/p&gt;<br />#    &lt;h3&gt;Guru Meditation:&lt;/h3&gt;<br />#    &lt;p&gt;XID: "} req.xid {"&lt;/p&gt;<br />#    &lt;address&gt;<br />#       &lt;a href="http://www.varnish-cache.org/"&gt;Varnish&lt;/a&gt;<br />#    &lt;/address&gt;<br />#  &lt;/body&gt;<br />#&lt;/html&gt;<br />#"};<br />#    return (deliver);<br />#}<br /><br /></div>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2010-02-18T21:45:00Z</dc:date>
    <dc:type>Page</dc:type>
  </item>


  <item rdf:about="http://www.nedproductions.biz/wiki/configuring-a-proxmox-ve-2.x-cluster-running-over-an-openvpn-intranet">
    <title>Configuring a Proxmox VE 2.x cluster running over an OpenVPN intranet</title>
    <link>http://www.nedproductions.biz/wiki/configuring-a-proxmox-ve-2.x-cluster-running-over-an-openvpn-intranet</link>
    <description>1. Prepare Proxmox VE 2.x
2. Configure the OpenVPN bridge on the public server
3. Move the Proxmox on the OpenVPN server over to running exclusively on the VPN private subnet
4. Adding additional Proxmox nodes
5. Configure a HTTP proxy so VMs can get security updates
6a. Final step: Make non-web services on VMs appear on the public interface
6b. Final step: Make web services on VMs appear on the public interface: haproxy, varnish, nginx
7. Setting up self-replicating node storage using DRBD
8. Configuring backing storage for the replicated data
9. Setting up DRBD
10. Adding the DRBD replicated storage to LVM so Proxmox can use it
11. Making the replicated storage available for OpenVZ as well
12. Accessing replicated content from one of the peer nodes
13. Encrypted off-site backup
</description>
    
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2012-02-22T23:55:00Z</dc:date>
    <dc:type>Folder</dc:type>
  </item>


  <item rdf:about="http://www.nedproductions.biz/wiki/replacing-plones-openid-login-with-an-openid-selector-for-google-yahoo-etc">
    <title>Replacing Plone's OpenID login with an OpenID selector for Google, Yahoo etc.</title>
    <link>http://www.nedproductions.biz/wiki/replacing-plones-openid-login-with-an-openid-selector-for-google-yahoo-etc</link>
    <description></description>
    
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2011-10-01T20:58:51Z</dc:date>
    <dc:type>Folder</dc:type>
  </item>


  <item rdf:about="http://www.nedproductions.biz/wiki/setting-up-a-content-caching-server">
    <title>Setting up a Content Caching Server</title>
    <link>http://www.nedproductions.biz/wiki/setting-up-a-content-caching-server</link>
    <description>How to use the wonderful varnish reverse proxy software to cache web server content elsewhere. Very useful for setting up a geographically distributed website or a low end Content Delivery Network (CDN).</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>I love the <a class="external-link" href="http://varnish.projects.linpro.no/" target="_blank">varnish reverse proxying software</a>: it's very fast, it's very scalable and it lets you perform magic with low end servers which would normally require you to rent expensive hardware costing you thousands of dollars. One of the single best parts of varnish is its wonderful and surprisingly powerful VCL configuration language which is compiled into C and then assembler by varnish - this makes it amazingly quick.</p>
<p>One of the very simplest, yet under-recognised, usages of varnish is as a dumb content caching solution which simply replicates the content of another website. This can allow you to spread the load off of one master website onto a series of round-robin slaves, or it could be used to provide <a class="internal-link" href="setting-up-a-geo-targeting-domain-name-server">a geo-localised copy of a server</a> such that each region gets much lower page load latencies. This particular example is orientated around Plone, so the cookies it checks for are the Plone login ones - you may need to change these for your particular CMS.</p>
<p>Anyway after an apt-get install varnish, and assuming that you're on Ubuntu or Debian, do nano /etc/default/varnish and replace the uncommented part with something like this:</p>
<pre>DAEMON_OPTS="-a :80 \<br />             -T localhost:6082 \<br />             -f /etc/varnish/default.vcl -u varnish -g varnish \<br />             -s file,/var/lib/varnish/$INSTANCE/varnish_storage.bin,1G"</pre>
<p>This simply puts varnish on port 80. You may wish to adjust the size of the cache - keep it well under the <i>guaranteed</i> RAM in your VPS unless you like stuttering. Oh, and don't exceed 1.5G if your VPS is 32 bit <img alt="Smile" src="../portal_javascripts/Plone%20Default/plugins/emotions/img/smiley-smile.gif" title="Smile" />.</p>
<p>After that do nano /etc/varnish/default.vcl and something similar to:</p>
<pre>backend default {<br />        .host = "europe1.nedproductions.biz";<br />        .port = "80";<br />        .first_byte_timeout = 300s; /* varnish v2.0.3 or later only */<br />        .probe = {<br />                .url = "/";<br />                .timeout = 1s;<br />                .interval = 60s;<br />                .window = 1;<br />                .threshold = 1;<br />        }<br />}<br /><br />/* Only permit cluster to purge files from cache */<br />acl purge {<br />        "europe1.nedproductions.biz";<br />        "usa1.nedproductions.biz";<br />        "localhost";<br />}<br /><br />sub vcl_recv {<br />        /* Before anything else we need to fix gzip compression */<br />        if (req.http.Accept-Encoding) {<br />                if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$")<br />                {<br />                        # No point in compressing these<br />                        remove req.http.Accept-Encoding;<br />                } else if (req.http.Accept-Encoding ~ "gzip") {<br />                        set req.http.Accept-Encoding = "gzip";<br />                } else if (req.http.Accept-Encoding ~ "deflate") {<br />                        set req.http.Accept-Encoding = "deflate";<br />                } else {<br />                        # unknown algorithm<br />                        remove req.http.Accept-Encoding;<br />                }<br />        }<br /><br />        if (req.request == "PURGE") {<br />                if (!client.ip ~ purge) {<br />                        error 405 "Not allowed.";<br />                }<br />                /* Always purge by URL rather than going via vcl_hash<br />                   as it hashes other factors which break purging */<br />                purge_url(req.url);<br />                error 200 "Purged";<br />        }<br /><br />        if (req.request != "GET" &amp;&amp;<br />                req.request != "HEAD" &amp;&amp;<br />                req.request != "PUT" &amp;&amp;<br />                req.request != "POST" &amp;&amp;<br />                req.request != "TRACE" &amp;&amp;<br />                req.request != "OPTIONS" &amp;&amp;<br />                req.request != "DELETE") {<br />                /* Non-RFC2616 or CONNECT which is weird. */<br />                pipe;<br />        }<br />        if (req.request != "GET" &amp;&amp; req.request != "HEAD") {<br />                /* We only deal with GET and HEAD by default */<br />                pass;<br />        }<br />        if (req.http.Cookie) {<br />                # We only care about the "__ac.*" cookies, used for<br />                # authentication and special persistent p_* cookies.<br />                if (req.http.Cookie ~ "__ac.*" ) {<br />                        pass;<br />                }<br />                # Else strip all cookies<br />                remove req.http.Cookie;<br />        }<br />        if (req.http.If-None-Match) {<br />                pass;<br />        }<br />        if (req.url ~ "createObject") {<br />                pass;<br />        }<br /><br />        /* Keep serving if haproxy goes down (ie; Plone is being serviced). Haproxy<br />        will return a 503 error page if Plone goes down for us */<br />        if (req.backend.healthy) {<br />                set req.grace = 120s; /* Only enable if you don't mind slightly stale content */<br />        } else {<br />                set req.grace = 24h;<br />        }<br /><br />        lookup;<br />}<br /><br />sub vcl_hit {<br />        if (req.request == "PURGE") {<br />                purge_url(req.url);<br />                error 200 "Purged";<br />        }<br />}<br />sub vcl_miss {<br />        if (req.request == "PURGE") {<br />                error 404 "Not in cache";<br />        }<br />}<br />sub vcl_fetch {<br />        set obj.grace = 24h; /* Keep at longest used in vcl_recv */<br />        set obj.http.Via = "usa1.nedproductions.biz";<br /><br />        if (req.url ~ "\.svgz$") {<br />                # Add a Content-Encoding to match compressed SVG<br />                set obj.http.Content-Type = "image/svg+xml";<br />                set obj.http.Content-Encoding = "gzip";<br />                remove obj.http.Content-Length;<br />                set obj.ttl = 86400s;<br />                set obj.http.Cache-Control = "max-age=3600";<br />                deliver;<br />        }<br />        if (obj.http.Set-Cookie) {<br />                pass;<br />        }<br />        if (req.http.Authorization &amp;&amp; !obj.http.Cache-Control ~ "public") {<br />                pass;<br />        }<br />        /* Only use this if you wish to override Plone's CacheFu */<br />        if (obj.ttl &lt; 3600s) {<br />                if (obj.http.Cache-Control ~ "(private|no-cache|no-store)") {<br />                        set obj.ttl = 60s; /* Caching everything anonymous for 60s is handy for being slashdotted :) */<br />                } else {<br />                        set obj.ttl = 3600s;<br />                }<br />        }<br />}<br /><br />sub vcl_error {<br />    set obj.http.Content-Type = "text/html; charset=utf-8";<br />    synthetic {" &lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"<br /> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt; &lt;html&gt;<br />  &lt;head&gt;<br />    &lt;title&gt;"} obj.status " " obj.response {"&lt;/title&gt;<br />  &lt;/head&gt;<br />  &lt;body&gt;<br />    &lt;div style="background-color:yellow;"&gt;<br />      &lt;h1&gt;This website is unavailable&lt;/h1&gt;<br />      &lt;p&gt;If you are seeing this page, either maintenance is being performed<br />      or something really bad has happened. Try returning in a few minutes.&lt;/p&gt;<br />      &lt;h2&gt;Error "} obj.status " " obj.response {"&lt;/h2&gt;<br />      &lt;p&gt;"} obj.response {"&lt;/p&gt;<br />      &lt;h3&gt;Guru Meditation:&lt;/h3&gt;<br />      &lt;p&gt;XID: "} req.xid {"&lt;/p&gt;<br />      &lt;address&gt;<br />         &lt;a href="http://www.nedproductions.biz/"&gt;ned Productions Ltd.&lt;/a&gt;<br />      &lt;/address&gt;<br />    &lt;/div&gt;<br />    &lt;div style="position:fixed;top:0;left:0;width:100%;height:100%;z-index:-1;"&gt;<br />    &lt;img alt="" src="http://www.nedproductions.biz/static/BBCTestCard.jpg" style="width:100%;height:100%" /&gt;&lt;/div&gt;<br />  &lt;/body&gt; &lt;/html&gt; "};<br />    return (deliver);<br />}<br /></pre>
<p>Obviously you'll need to replace the backend with your own - however remember that varnish can take multiple backends and it doesn't really care if they belong to you or not, so you can do such magic as mount parts of other websites as subdirectories of your own website (and cached to boot!) which is a real boon for AJAX programming as it avoids cross-site scripting issues.</p>
<p>What the above config does is to simply cache the entire backend content on the local server. We make use of the grace feature which allows varnish to serve stale content - this is where varnish will immediately serve the cached content whether it is fresh or not and then initiates an asynchronous refresh from the backend. If the backend returns a 302 Not Modified, or new content, the cache entry is reset  as fresh for the next visitor and will expire whenever its Time To Live (TTL) does. The grace factor is set according to the backend's healthiness - if it is responding to requests then varnish will only return cached content for thirty seconds after it has requested a new copy from the backend, and should that request not be answered within thirty seconds then the cached copy is deleted. If it is not healthy then the grace period is extended to twenty-four hours e.g. if the backend goes down for an extended time.</p>
<p>The idea through these settings is that the cache keeps as close as possible to emulating the backend, so if the backend is working but being funny then the cache ought to replicate that funniness instead of smoothing out the kinks.</p>
<p>I have some helpful fixes in these which I have discovered through experience. One is to harmonise Content-Encoding and delete Cookies so the varnish cache doesn't end up with multiple copies of the same data - it hashes both the Content-Encoding and Cookies, so any minor difference at all will cause data duplication which is inefficient. I have the possibility for the cache to be purged, so in Plone you can add a second purge destination in CacheFu such that when Plone purges the primary varnish cache it also purges the dumb cache. One could actually have the primary varnsh cache do this for you by trapping purge requests and firing them at a backend which is actually your dumb cache - this has the convenience of extra transparency and less config hassle in Plone.</p>
<p>Lastly I have a hack in here to remedy compressed SVGZ not working properly in some browsers - I simply retype to SVG and set gzip-encoded. And the last part forces a cache Time-To-Live of one hour if something is set as cacheable but is to be cached for less than one hour, or if it's uncached then it is set to sixty seconds which makes one hell of a difference if you ever get DDoS'd or Slashdotted.</p>
<p>Note that authenticated sessions are ALWAYS passed through unmodified to the backend. This is a dumb cache, so really it's only for anonymous visitors. Most of your scalability issues will always tend to stem from too many logged in users anyway on most websites.</p>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 578px; width: 1px; height: 1px; overflow: hidden;">
<pre>        </pre>
</div>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>Content Caching</dc:subject>
    
    
      <dc:subject>Web Server</dc:subject>
    
    <dc:date>2010-02-01T11:57:17Z</dc:date>
    <dc:type>Page</dc:type>
  </item>


  <item rdf:about="http://www.nedproductions.biz/wiki/setting-up-a-geo-targeting-domain-name-server">
    <title>Setting up a Geo-Targeting Domain Name Server</title>
    <link>http://www.nedproductions.biz/wiki/setting-up-a-geo-targeting-domain-name-server</link>
    <description>Detailed guide describing how to set up a simple but powerful geo-targeting Domain Name Server using the open source PowerDNS server</description>
    
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>GeoIP</dc:subject>
    
    
      <dc:subject>Geo-Directional</dc:subject>
    
    
      <dc:subject>Web Server</dc:subject>
    
    
      <dc:subject>DNS</dc:subject>
    
    <dc:date>2010-01-30T21:02:51Z</dc:date>
    <dc:type>Folder</dc:type>
  </item>





</rdf:RDF>
