Force redirect in Varnish

Loading

Sometimes in certain occasion you need to manage a complete redirection to an external site directly from Varnish.

Following you will find how to implement this using Varnish 4.0 and some specific rules in the VCL.


sub force_redirect {
   set req.http.host = "www.atomictag.com";
   return(synth(750, "Force redirection to external site."));
}

sub vcl_recv {
   # Force redirect
   call force_redirect;
   # Everything else will be ignored
}

sub vcl_synth {
   # Managing redirection
   if (resp.status == 750) {
      set resp.status = 301;
      set resp.http.Location = "http://www.atomictag.com/";
      return(deliver);
   }
}

Of course you can easily add more intelligence to the VCL, e.g.: by evaluating the redirection based on the http referrer or on a specific hostname.

Normalize trailing slash for directory and manage 301 redirection internally

Loading

Add the following code into VCL_RECV:


vcl 4.0;
import std;

sub vcl_recv { 

  # Normalize trailing slash for directory & manage 301 redirection internally
  if (req.http.Host !~ "^www\." || 
              (req.url !~ {"(?x)
               (?:/$)         # last character isn't a slash
               |              # or
               (?:/\?)        # query string isn't immediately preceded by a slash
               "} 
               &&
               req.url ~ {"(?x)
               (?:/[^./]+$)   # last path segment doesn't contain a . no query string
               |              # or
               (?:/[^.?]+\?)  # last path segment doesn't contain a . with a query string
               "}
              )
      ) 
    {  
        return (synth(720,"Moved Permanently"));
    }
}

Add the following code into VCL_SYNTH:


sub vcl_synth {
    
  if (resp.status == 720) {
    # We use this special error status 720 to force redirects with 301 (permanent) redirects
    # To use this, call the following from anywhere in vcl_recv: return (synth(720, "http://host/new.html"));
    set resp.http.Location = resp.reason;
    set resp.status = 301;
    
    set resp.http.Location = "http://";
    set resp.http.Host = req.http.Host;
    
    if (resp.http.Host !~ "^www\.") { 
        set resp.http.Host = "www." + resp.http.Host;
    }
    
    set resp.http.Location = resp.http.Location + resp.http.Host;

    if (req.url ~ "(?:/[^./]+$)|(?:/[^.?]+\?)") {
        # no . in last path segment before optional query string
        if (req.url !~ "/$" && req.url !~ "\?") {
            # no trailing slash and no query string
            set resp.http.Location = resp.http.Location + req.url + "/";
        } elseif (req.url ~ "[^/]\?") {
           # no trailing slash and with query string, preserve it
           set resp.http.Location = resp.http.Location + regsub(req.url, "([^?]+)\?.*", "\1") + "/" + regsub(req.url, "[^?]+(\?.*)", "\1");
        } elseif (resp.http.Host != req.http.Host) {
           # trailing slash rule met, handle missing www. scenario
           set resp.http.Location = resp.http.Location + req.url;
        }
      } elseif (resp.http.Host != req.http.Host) {
            # last path segment contains a . so handle missing www. scenario
            set resp.http.Location = resp.http.Location + req.url;
        }
        
  return (deliver);
    
  } elseif (resp.status == 721) {
    # And we use error status 721 to force redirects with a 302 (temporary) redirect
    # To use this, call the following from anywhere in vcl_recv: return (synth(720, "http://host/new.html"));
    set resp.http.Location = resp.reason;
    set resp.status = 302;
    return (deliver);
  }

  return (deliver);
}