The ultimate guide on Reverse Proxy: manage your services like a boss

A mess of wires and electrical equipment

A Reverse Proxy is a type of proxy that handles requests on behalf of clients by retrieving resources from one or multiple servers. As web apps become ubiquitous the Reverse Proxy become essential, especially with newer architectures such as Microservice or Service-Oriented.

Reverse Proxy: the big picture.

Before diving too deep into the subject I suggest you read my Ultimate Guide on Virtual Hosts. Already know your way around Virtual Hosts? Let’s get started!

Let’s define three distinct entities:

  • The service: just any site or web app.
  • The webserver: such as Nginx or Apache.
  • The reverse proxy.

With this let’s define three scenarios in which you have three different services to host.

Older architectures: One webserver per service

In older architectures for each service you needed a different webserver was used. While this could be mitigated using directories, sometimes it was not allowed or possible. This leads to the three different webservers need for three different IP addresses!

Virtual hosts: One webserver multiple services

To reduce the number of servers and IP addresses needed to host multiple services (name-based) Virtual Hosts were created. In this scenario you still have three services but they are hosted on the same physical server (keep in mind this point) and a single webserver serves the three services. While you reduced the number of webservers/IP addresses, the webserver is now responsible for multiple sites and each one could be very heavy.

Imagine three sites with high traffic, each one needs 16GiB of RAM to run. The physical host will need to have at least 48GiB of RAM to host the three sites simultaneously (plus memory for other things and the OS).

Reverse proxy: the modern approach

In this scenario you still have three services, each one is served by its own webserver on a different physical host.

  • Since they have three different addresses (not necessarily public ones) you can’t easily implement Virtual Hosts without transferring them in the same physical host.
  • If you assign three different public IP addresses you go back to the “old architecture”.

That’s where a wild Reverse Proxy appears. In this scenario the only Internet-facing server (with a public IP) proxies the traffic to the three webservers serving the services.

Although you need yet another machine there are multiple benefits to this such as:

  • Increased scalability
  • Possibility to terminate TLS connections in a single place
  • Increased security (provided you should secure the proxy AND the server anyway)
  • Possibility to add caching and load-balancing depending on the software

How to choose a Reverse Proxy

There is a number of proxies available you can choose from:

  • Nginx (suggested)
  • HAProxy
  • Apache
  • Caddy
  • Lighthttpd

And probably there are a few more. If you don’t have peculiar requirements, any of these will do the trick. If you’re unsure, go for Nginx: you won’t be disappointed. If you need higher performance and the ability to implement load-balancing HAProxy is a great tool. I would avoid Apache if possible, nothing wrong with it, but Nginx and HAProxy are usually more efficient.

Reverse Proxy with Nginx

Nginx is by far THE go-to software for setting up a reverse proxy. You simply need to insert the following within a server block:

location / {
  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;
  proxy_pass http://YOUR_SERVER:YOUR_PORT;
}

The first line is extremely important. You won’t be able to serve multiple root directories (unless you use multiple server blocks). If you want to access your site through REVERSE_PROXY_IP/DIRECTORY simply change line one to: location /DIRECTORY {. The second important line is the sixth one. The argument of proxy_pass is the address of one of the webservers listening. All the other lines are needed by the webserver behind the proxy to identify the client. If those headers are not set it will look like the only client speaking to your webserver is the proxy.

In order to host multiple services behind a proxy you will need to insert the code above for each service/webserver.

Nginx example using directories

In this example you will access the services through: PROXY_IP_OR_FQDN/site1, REVERSE_IP_OR_FQDN/site2, REVERSE_IP_OR_FQDN/site3.

server {
  listen 80;
  server_name PROXY_IP_OR_FQDN;
  location /site1 {
    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;
    proxy_pass http://site1.example.local;
  }
  location /site2 {
    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;
    proxy_pass http://site2.example.local;
  }
  location /site3 {
    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;
    proxy_pass http://site3.example.local;
  }
}

Nginx example with multiple server blocks

In this example you will access the services through: site1.example.com/, site2.example.com/, site3.example.com/.

server {
  listen 80;
  server_name site1.example.com;
  location / {
    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;
    proxy_pass http://site1.example.local;
  }
}

server {
  listen 80;
  server_name site2.example.com;
  location / {
    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;
    proxy_pass http://site2.example.local;
  }
}

server {
  listen 80;
  server_name site3.example.com;
  location / {
    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;
    proxy_pass http://site1.example.local;
  }
}

Reverse Proxy with Apache

Apache is an unusual (yet possible) choice when it comes to reverse proxy. In order for this to work you need to install mod_proxy, I’ll assume you know how to do it. You simply need to add the following within the root configuration or a VirtualHost (highly suggested):

<Location />
  ProxyPass "http://YOUR_SERVER:YOUR_PORT/"
  ProxyPassReverse "http://YOUR_SERVER:YOUR_PORT/"
</Location>

If you want to access your site through REVERSE_PROXY_IP/DIRECTORY simply change the first line one to: <Location /DIRECTORY>. The second important line is the sixth one. The argument of ProxyPass is the address of one of the webservers listening.

As with Nginx, you will need to insert the code above for each service/webserver you want to proxy.

Apache example using directories

In this example you will access the services through: PROXY_IP_OR_FQDN/site1, REVERSE_IP_OR_FQDN/site2, REVERSE_IP_OR_FQDN/site3.

<VirtualHost *:80>
  ServerName REVERSE_IP_OR_FQDN
  DocumentRoot /some/document/root

  <Location /site1>
    ProxyPass "http://site1.example.local/"
    ProxyPassReverse "http://site1.example.local/"
  </Location>

  <Location /site2>
    ProxyPass "http://site2.example.local/"
    ProxyPassReverse "http://site2.example.local/"
  </Location>

  <Location /site3>
    ProxyPass "http://site3.example.local/"
    ProxyPassReverse "http://site3.example.local/"
  </Location>
</VirtualHost>

Apache example using multiple Virtual Hosts

In this example you will access the services through: site1.example.com/, site2.example.com/, site3.example.com/.

<VirtualHost *:80>
  ServerName site1.example.com
  ProxyPreserveHost On

  <Location />
    ProxyPass "http://site1.example.local/"
    ProxyPassReverse "http://site1.example.local/"
  </Location>
</VirtualHost>

<VirtualHost *:80>
  ServerName site2.example.com
  ProxyPreserveHost On

  <Location />
    ProxyPass "http://site2.example.local/"
    ProxyPassReverse "http://site2.example.local/"
  </Location>
</VirtualHost>

<VirtualHost *:80>
  ServerName site3.example.com
  ProxyPreserveHost On

  <Location />
    ProxyPass "http://site3.example.local/"
    ProxyPassReverse "http://site3.example.local/"
  </Location>
</VirtualHost>

More to come:

  • Some pretty figures to outline the three scenarios.
  • HAProxy configuration.
  • TLS termination (either server <-> proxy or proxy only).
  • Let’s Encrypt!
  • Some more exotic proxies configurations (lighthttpd, caddy).
Image courtesy of mark | marksei
mark

You may also like...

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.