Nginx Reverse Proxy based on user-agent

Problem:

I run a 'load balancer' at home. The first thing I hear when I tell people this is something along the lines of 'what on earth are you running if you need a load balancer at home?!' Now technically it's not really a load balancer it's a reverse proxy.

The problem I have was actually solved in an earlier blog post however I thought it might be useful to provide some documentation on the issue. The associated post is HERE.

Essentially because I have a script which needs to validate my domain ownership every few months I need to put some files in a web root. My blog is my default site within my nginx 'load balancer' so how can I do this nicely in a programmatic way?

Solution:

Well, the best way I could think up essentially uses the user-agent of the validator LetsEncrypt to route any requests from LetsEncrypt to a different site, which holds just raw files.

Here is the config before:

upstream blog 
{
	server ghost:2368;
}

server 
{
	listen 80 default;
	
	server_name blog.dchidell.com *.dchidell.com;

	location / 
	{
		proxy_pass http://blog;
		proxy_set_header Host $host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Proto $scheme;
	}
}

Pretty standard stuff. Anything that comes in on the default or blog domains is forwarded to the ghost instance. Easy enough!

Now, we need to make sure that if anything from letsencrypt comes in they're redirected elsewhere. Here's the new config:

upstream blog 
{
	server ghost:2368;
}

upstream blog_files
{
	server webserver;
}

server 
{
	listen 80 default;
	
	server_name blog.dchidell.com *.dchidell.com;

	location / 
	{
		proxy_pass http://blog;
		if ($http_user_agent ~ www.letsencrypt.org ) 
		{
			proxy_pass http://blog_files;
		}
		proxy_set_header Host $host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Proto $scheme;
	}
}

Now we have a few new parts:

upstream blog_files
{
	server webserver;
}

This chunk defines where we want the new upstream information to point.

if ($http_user_agent ~ www.letsencrypt.org ) 
{
	proxy_pass http://blog_files;
}

And here is where we check for the user-agent, if it's letsencrypt we'll redirect it to the webserver definition we made above.

That is literally it, really nice and simple!