varnish referencing an external file - varnish-vcl

I am new to varnish and I am trying to figure out a way in which varnish can reference to an external html file to serve an error page, as having the html code in the synthetic function would be too complex as the error page has too many visualizations, thanks

If you are using Varnish 4 (or more recent) you can do it with the std vmod
See the doc : https://www.varnish-cache.org/docs/4.0/reference/vmod_std.generated.html#func-fileread
I think the vcl should look like the following (not tested) :
vcl 4.0;
import std;
#other stuff
sub vcl_synth {
if (resp.status == 404) {
set resp.http.Content-Type = "text/html;";
synthetic(std.fileread("/etc/varnish/404.html"));
return (deliver);
}
}

Related

How to set up a hash based director using vcl in Varnish which is using the hashed request body?

I am trying to set up Varnish to route between backends using the request body's hash. I found good examples of using body access to set up caching where the hash of the request body is used as cache key. I cannot find an example of using the body hash for routing.
I tried the following but it doesn't seem to work. It is probably because bodyaccess was not meant to be used this way. How to set up a hash based director using vcl in Varnish which is using the hashed request body?
vcl 4.1;
import directors;
import bodyaccess;
backend backend1 {
.host = "backend1.example.com";
.port = "80";
}
backend backend2 {
.host = "backend2.example.com";
.port = "80";
}
sub vcl_init {
new xhash = directors.hash();
xhash.add_backend(backend1);
xhash.add_backend(backend2);
}
sub vcl_recv {
set req.backend_hint = xhash.backend(bodyaccess.hash_req_body());
}
There is no real way to retrieve the request body as a string in Varnish Cache (open source). The bodyaccess.hash_req_body() function will actually add the request body to the caching hash in the vcl_hash subroutine. But since this function returns a void data type, this won't help you.
The only realistic way that I'm aware of is by using vmod_xbody, which is a Varnish Enterprise module. That module has an xbody.get_req_body() function that returns the request body as a string.
See https://docs.varnish-software.com/varnish-cache-plus/vmods/xbody/#get-req-body for more information.

is it possible to change response on proxy level using varnish?

For example we have setup like this:
user -> api gateway -> (specific endpoint) varnish -> backend service
If backend returns response 500 {"message":"error"} I want to patch this response and return 200 "[]" instead.
Is it possible to do something like this using varnish or some other proxy?
It is definitely possible to intercept backend errors, and convert them into regular responses.
A very simplistic example is the following:
sub vcl_backend_error {
set beresp.http.Content-Type = "application/json";
set beresp.status = 200;
set beresp.body = "[]";
return(deliver);
}
sub vcl_backend_response {
if(beresp.status == 500) {
return(error(200,"OK"));
}
}
Whenever your backend would fail, and return an HTTP/503 error, we will send a HTTP/200 response with [] output.
This output template for backend errors is also triggered when the backend does reply, but with a HTTP/500 error.
In real world scenarios, I would a some conditional logic in vcl_backend_error to only return the JSON output template when specific criteria are matched. For example: a certain URL pattern was matched.
I would advise the same in vcl_backend_response: maybe you don't want to convert all HTTP/500 errors into regular HTTP/200 responses. Maybe you also want to add conditional logic.

Varnish remove from hash_data

Is it possible to remove an item from the hash which has already been added via a previous hash_data() call?
I have a varnish 3 server running in front of a standard LAMP stack and using the Maxmind geoip VMOD per the varnish documentation except there is one included VCL file that I don't have permission to edit which basically has:
sub vcl_hash {
if (req.http.X-Geo-Country) {
hash_data(req.http.X-Geo-Country);
}
}
I know I can call vcl_hash multiple times and they concatenate in order so what I'd like to do is something like the below (un_hash_data doesn't exist) so that only the homepage hashes based on req.http.X-Geo-Country.
Is there something like un_hash_data() which can remove this from the hash.
sub vcl_hash {
if (req.url == "/") {
un_hash_data(req.http.X-Geo-Country);
}
}
In vcl_recv you can unset req.http.X-Geo-Country for the pages/requests that don't match homepage. If X-Geo-Country is not set/empty - will make the same hash :)

MVC Permanent way to use redirects for HTTP to HTTPS and turn off for Dev Environment

I got this code from here.
Notice I remmed out the part that redirects to ISSExpress 44300 port because I want to use II7.5 on dev box without https.
public class CustomRequireHttpsFilter : RequireHttpsAttribute
{
protected override void HandleNonHttpsRequest(AuthorizationContext filterContext)
{
// The base only redirects GET, but we added HEAD as well. This avoids exceptions for bots crawling using HEAD.
// The other requests will throw an exception to ensure the correct verbs are used.
// We fall back to the base method as the mvc exceptions are marked as internal.
if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)
&& !String.Equals(filterContext.HttpContext.Request.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase))
{
base.HandleNonHttpsRequest(filterContext);
}
// Redirect to HTTPS version of page
// We updated this to redirect using 301 (permanent) instead of 302 (temporary).
string url = "https://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
//if (string.Equals(filterContext.HttpContext.Request.Url.Host, "localhost", StringComparison.OrdinalIgnoreCase))
// {
// // For localhost requests, default to IISExpress https default port (44300)
// url = "https://" + filterContext.HttpContext.Request.Url.Host + ":44300" + filterContext.HttpContext.Request.RawUrl;
// }
filterContext.Result = new RedirectResult(url, true);
}
}
Then, in my FilterDonfig.cs I added this. What it does is it only uses the override above if Web.config has "Debug=false", which is what it has in Production. I don't need to run Release in my development environment, and I also don't want configure local IIS to handle SSL. Notice I remmed out the "RequireHttpsAttribute()" and used the new one above.
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
if (!HttpContext.Current.IsDebuggingEnabled)
{
/////filters.Add(new RequireHttpsAttribute());
filters.Add(new CustomRequireHttpsFilter());
}
}
}
Am I doing the right thing? Is this how to make sure SEO is optimized because search bots only keep track of one website? My understanding is that "http" and "https" are considered 2 separate websites by search engines. Am I doing this in the right place? Not sure what other code I am getting in the way of.
===============
I asked my ISP about how to do permanent redirects and suggested this solution and they said:
Dear Customer,
We did not setup redirection. However, we corrected https bind setting in IIS to fix the problem.
I wonder if IIS can do the same thing and that is what they did. I hope I'm in the right forum :)
How about doing this at an IIS level using the URL rewrite module: http://forums.iis.net/t/1153050.aspx?URL+Rewrite+for+SSL+redirection
To turn it off in dev, just set the enabled rule to false in your dev web.config, but enable it for all servers/environments that have HTTPS set up.
I've used it in the past and its worked really well. Saves cluttering your app with code that isn't app related.

Changing URLs while moving project from development server to live server

This is relatively a noob question.
While developing a ASP.Net website, if I am referring to a resource with ResolveUrl() method, it works on either live server or dev server but not on both, based on the Url provided.
For example, if my code tries to pick a resource with below code, it works on live server but not on development server as JScript.js is not under http://localhost:xx/Assets but is under http://localhost:xx/ApplicationName/Assets.
<script src='<%# ResolveUrl("~/Assets/JScript.js")%>' type="text/javascript"></script>
In order to make it work on both servers, I have to keep changing the URL according to the server I am working on.
I have been suffering this annoying problem for sometime but kept ignoring it.
Is there a better way to do it?
Thanks!
As posted in my comments above, you want to use a <%= %> vs. <%# %> to render an inline expression. You only use the hash symbol for inline data binding.
I know this might not be the out-the-box way, but I use these to make sure URLS are corect within my applications without issues. with these available in my Page/View's base classes...
public static string ApplicationRootUrl()
{
string port = String.Empty;
if (HttpContext.Current.Request.ServerVariables["SERVER_PORT"] != null && HttpContext.Current.Request.ServerVariables["SERVER_PORT"].ToString() != "80" && HttpContext.Current.Request.ServerVariables["SERVER_PORT"].ToString() != "443")
{
port = String.Concat(":", HttpContext.Current.Request.ServerVariables["SERVER_PORT"].ToString());
}
string protocol = "http://";
if (HttpContext.Current.Request.ServerVariables["SERVER_PORT_SECURE"] != null && HttpContext.Current.Request.ServerVariables["SERVER_PORT_SECURE"] != "0")
{
protocol = "https://";
}
return String.Concat(protocol, String.Concat(HttpContext.Current.Request.Url.Host, port, HttpContext.Current.Request.ApplicationPath, '/').Replace(#"//", #"/").ToLower());
}
/// <summary>
/// Expands a virtual URL to an absolute URL using the current application root url
/// </summary>
/// <param name="url"></param>
public static string ExpandUrl(string url)
{
if (url.Trim().StartsWith("~"))
{
return String.Concat(ApplicationRootUrl(), url.Substring(1).Replace("//", "/"));
}
if (url.Trim().StartsWith("www", StringComparison.OrdinalIgnoreCase))
{
return String.Concat("http://", url);
}
return url;
}
Try making http://localhost:xx/ApplicationName a virtual app on the DEV machine. That way ~/Assets will be in the root of the app on both PROD and DEV
Basically, the "Assets" directory needs to be in the root of your App, so you need to make the parent of "Assets" the App root on both Dev and PROD.

Resources