Route suburls to different origin servers?

is it possible to route a domain differently depending on what the URL is?

e.g.:

/ → backblaze B2
/api/v1/ → v1 api server
/api/v2/ → v2 api server

1 Like

Hi @nothingwobbles

Yes! This is possible.

We have examples of this on the Fastly Developer Hub:
developer.fastly.com/solutions/examples/url-path-based-routing-for-microservices

There you’ll find examples of doing this for both a ‘Deliver’ service (VCL) or a ‘Compute’ service (e.g. Rust, Go, JavaScript etc).

Hope this helps.

Any other questions, just let us know.

Thanks!

1 Like

Thanks Mark! Did that resolve your question, @nothingwobbles?

not really, followed the guide, it’s still broken. :upside_down_face:

ah, the “add a custom snippet” thing claims it adds it at the end of the routines. It doesn’t. See:

  declare local var.fastly_req_do_shield BOOL;
  set var.fastly_req_do_shield = (req.restarts == 0);
# Snippet rcv : 100 ## my code
if (req.url ~ "^/api(/[^?]*)?(\?.*)?$") {
    set req.backend = F_nanovm;
} else {
  set req.backend = F_Host_1;
} ## end my code
  # default conditions ## your code
  set req.backend = F_Host_1;
  if (!req.http.Fastly-SSL) {
     error 801 "Force SSL";
  }

Hi @nothingwobbles

Thanks for getting back to us.

It looks like you’re using the Fastly UI to add the VCL snippet to your service, and I too have been able to replicate what you’re experiencing (see ‘Explanation’ section below).

Problem

The problem is the VCL snippet is being added to an area of the vcl_recv subroutine which is resulting in the if statement in your snippet being made redundant because Fastly inserts any defined backends after the VCL Snippet.

Solution

Firstly, delete the VCL Snippet you added via the Fastly UI (e.g. click on “VCL snippets” and click the trashcan/bin icon to delete the snippet).

We need to use “Custom VCL” to ensure the VCL snippet is placed in a more suitable location of the vcl_recv subroutine. To do that, follow these steps in the Fastly UI:

  1. Click on “Custom VCL”.
  2. Click on “Upload your first VCL file”.
  3. Add “main” into the Name field.
  4. In the ‘Edit VCL’ input field add the following VCL code…
sub vcl_recv {
  #FASTLY recv
  
  # Requests for /status and all subpaths => origin 0
  # (query strings allowed)
  if (req.url ~ "^/status(/[^?]*)?(\?.*)?$") {
    set req.backend = F_Host_1;
  # Requests for exactly / => origin 1
  # (query strings not allowed)
  } else if (req.url == "/") {
    set req.backend = F_Host_2;
  # Unrecognised path => synthethic 404 error
  } else {
    error 601;
  }
  
  if (req.request != "HEAD" && req.request != "GET" && req.request != "FASTLYPURGE") {
    return(pass);
  }
  return(lookup);
}

sub vcl_error {
  #FASTLY error

  if (obj.status == 601) {
    set obj.status = 404;
    set obj.response = "Not found";
    set obj.http.Content-Type = "text/html; charset=utf8";
    synthetic "Page not found";
    return (deliver);
  }

  return(deliver);
}

This is effectively the same VCL as shown in the examples page I linked originally.

Now if you click on “Show VCL” you should see the correct vcl_recv being generated.

Explanation

For anyone else following along with this discussion, please see the below steps that explain in detail how to replicate the issue and an explanation for why this is happening.

Specifically, the problem is the VCL snippet is being added to an area of the vcl_recv subroutine which is resulting in the if statement being made redundant because Fastly inserts any defined backends after the VCL Snippet.

Below are the steps I’ve taken (using the Fastly UI) to replicate the issue:

  1. I create a new ‘Deliver’ service.

  2. I click on “Show VCL”. I then look for the vcl_recv subroutine and I find the following ‘default’ VCL added to my service…

sub vcl_recv {
  #--FASTLY RECV BEGIN
  
    if (req.restarts == 0) {
      if (!req.http.X-Timer) {
        set req.http.X-Timer = "S" time.start.sec "." time.start.usec_frac;
      }
      set req.http.X-Timer = req.http.X-Timer ",VS0";
    }
    declare local var.fastly_req_do_shield BOOL;
    set var.fastly_req_do_shield = (req.restarts == 0);

    # default conditions
    # end default conditions
  
  #--FASTLY RECV END
  
  if (req.request != "HEAD" && req.request != "GET" && req.request != "FASTLYPURGE") {
    return(pass);
  }
  return(lookup);
}
  1. I add two hosts (httpbin.org and example.com).

  2. I click on “Show VCL”. I again look for the vcl_recv subroutine and I find the VCL has changed slightly (e.g. the # default conditions section now contains a ‘default’ backend being set, it has selected the first backend of the two I added in the previous step)…

sub vcl_recv {
  #--FASTLY RECV BEGIN
  
    if (req.restarts == 0) {
      if (!req.http.X-Timer) {
        set req.http.X-Timer = "S" time.start.sec "." time.start.usec_frac;
      }
      set req.http.X-Timer = req.http.X-Timer ",VS0";
    }
    declare local var.fastly_req_do_shield BOOL;
    set var.fastly_req_do_shield = (req.restarts == 0);

    # default conditions
    set req.backend = F_Host_1;
    # end default conditions
  
  #--FASTLY RECV END
  
  if (req.request != "HEAD" && req.request != "GET" && req.request != "FASTLYPURGE") {
    return(pass);
  }
  return(lookup);
}
  1. I click on “VCL Snippet” in the menu and create a new VCL Snippet. I select the “within subroutine” option and select vcl_recv as the subroutine to insert the snippet into.

NOTE: The description in the page says “(following any boilerplate code and preceding any objects)”, now this isn’t very clear if you don’t know what “objects” actually means. So in this case I happen to know that creating a host is effectively creating a ‘backend object’ so I’m going to presume that my snippet will be placed in-between the #--FASTLY RECV BEGIN macro block and the # default conditions block. Which as has already been explained, means the example won’t work because the # default conditions block is going to override my VCL snippet logic. This means we’ll ultimately need Custom VCL to ensure the VCL Snippet is placed into a more appropriate location (e.g. after where the default backend is set).

  1. I click on “Show VCL”. I again look for the vcl_recv subroutine and, as expected (see my above ‘NOTE’) I find the VCL has changed and the snippet is set in a location which means my service won’t work as expected…
sub vcl_recv {
  #--FASTLY RECV BEGIN
  
    if (req.restarts == 0) {
      if (!req.http.X-Timer) {
        set req.http.X-Timer = "S" time.start.sec "." time.start.usec_frac;
      }
      set req.http.X-Timer = req.http.X-Timer ",VS0";
    }
    declare local var.fastly_req_do_shield BOOL;
    set var.fastly_req_do_shield = (req.restarts == 0);

    # Snippet Test Snippet : 100

      # Requests for /status and all subpaths => origin 0
      # (query strings allowed)
      if (req.url ~ "^/status(/[^?]*)?(\?.*)?$") {
        set req.backend = F_Host_1;
      # Requests for exactly / => origin 1
      # (query strings not allowed)
      } else if (req.url == "/") {
        set req.backend = F_Host_2;
      # Unrecognised path => synthethic 404 error
      } else {
        error 601;
      }

    # default conditions
    set req.backend = F_Host_1;
    # end default conditions
  
  #--FASTLY RECV END
  
  if (req.request != "HEAD" && req.request != "GET" && req.request != "FASTLYPURGE") {
    return(pass);
  }
  return(lookup);
}
1 Like