URL Based Variables in Nginx

Over the past few months, I’ve set up a few fairly complex staging environments for websites I’ve been working on.

One setup creates a new subdomain based on the ticket number so we can test just that branch of code. If the ticket number is ticket-123, the testing url might look something like ticket-123.staging.example.com. I have Jenkins set up to create a directory for each site at something like /var/www/html/ticket-123.

Another setup is a staging installation for a large multisite install that utilizes domain mapping, so there are many different domains all on the same multisite install (site1.com, site2.com, and site3.com). The staging server for this clones the production database, does some magic on the urls, and I end up with staging urls like site1.staging.example.com, site2.staging.example.com, and site3.staging.example.com. To save some disk space and avoid the headache of copying a bunch of media every time we move the database from production to staging, I proxy the images from the production site.

All of this could be set up manually, but creating a new nginx config file each time a new ticket is staged or having to set up a separate rules for each site we want to proxy images for on the multisite would be tedious work.

Here’s how I solved these issues.

Nginx allows you to use regular expressions in your server_name line. In addition, you can capture certain parts of the url for use later, by giving them a name. Here’s an example of how I match for a ticket number based on a URL structure that looks like ticket-123.staging.example.com

server {
    server_name  ~^(?P<ticket>.+)\.staging\.example\.com$
}

The above should match any subdomain on staging.example.com and store the preceding segment of the URL in the $ticket variable. Now that I have the $ticket variable, I can use this information to point nginx to the correct site root.

server {
    server_name  ~^(?P<ticket>.+)\.staging\.example\.com$
    root    /var/www/html/$ticket;
}

Now any request that comes in for a staged ticket will automatically serve the files from the correct location.

Multisite Image Proxy

The multisite install uses similar techniques for a different end result. In this case, we are only ever staging one codebase at a time (not different tickets), but there are a bunch of images that we want to proxy from the production server. Here’s the catch – the production urls vary, because the main site uses domain mapping. Here’s an example of how the URLs translate from production to staging

  • www. site1.com -> site1.staging.example.com
  • www.site2.com -> site2.staging.example.com
  • www.site3.com-> site3.staging.example.com

Luckily there is a pattern to how the URLs change, so this is a problem I was able to solve again using the named variable capture in Nginx.

Here’s an example of what the server name looks like in the Nginx config (It nearly identical to above)

server {
    server_name  ~^(?P<subsite>.+)\.staging\.example\.com$
}

Again, now that I have the $subsite variable available, I can use that to construct the URL to proxy images from (See this post for more on proxying images with Nginx).

Here’s what the nginx config looks like to accomplish the smart image proxy

server {
    server_name  ~^(?P<subsite>.+)\.staging\.example\.com$

    location ~* ^.+\.(svg|svgz|jpg|jpeg|gif|png|ico|bmp)$ { {
        try_files $uri @image_fallback;
    }

    location @image_fallback {
        resolver 8.8.8.8;
        proxy_pass http://www.$subsite.com;
    }
}

 

One thought on “URL Based Variables in Nginx

Leave a Reply

Your email address will not be published. Required fields are marked *