Hiding your web server behind proxy

In one of my previous blog posts I mentioned hiding your server behind proxy to protect yourself from long downtime.

Since some of you wanted more information, I figured I should write about it. This will still only scratch the surface of it and you should do some additional reading if you want to get a better understanding of how these things work and what they can be used for.

Let’s begin!

The idea behind this tutorial is that you have 2 servers. One is used as a proxy and static content provider (yes, this is the box your other server is  hiding behind) and the other one is the server you keep your actual site on (database, scripts, etc.).

When user requests page on your site, proxy server passes this request to the backend server. Backend server then generates the page and sends it back to the proxy. Proxy then returns the page to the user requesting it.

To make it a bit faster, static content like images, CSS and javascript files are stored on your proxy server. These static files are synchronised from backend.

Also, to make it more secure and fast, multiple SSH tunnels are used between proxy and backend. All page requests from proxy to backend go through these SSH tunnels and web server on the backend listens for connections only locally so the site would not be accessible by making request to the backend directly.

Following diagram illustrates how it works:

Remember to tunnel everything, including your SMTP (e-mail) and possibly some other traffic. I already wrote about mini_sendmail that can help you tunneling SMTP.

Also, log files on the proxy server should be disabled and backend server IP address should not appear in any configuration file on the proxy server.

In my case I used Nginx as web server and proxy, rsync as the tool to synchronise static files between the servers, autossh to keep tunnels open, mini_sendmail to help tunnel SMTP.

To start the reverse tunnel using Autossh, use something like this on backend:

export AUTOSSH_PIDFILE=/tmp/tun1.pid && autossh -M 2001 -fNgR 5101: [email protected]
export AUTOSSH_PIDFILE=/tmp/tun2.pid && autossh -M 2003 -fNgR 5102: [email protected]
export AUTOSSH_PIDFILE=/tmp/tun3.pid && autossh -M 2005 -fNgR 5103: [email protected]
export AUTOSSH_PIDFILE=/tmp/tun4.pid && autossh -M 2007 -fNgR 5104: [email protected]

I would make some script to easily start or stop them all at once and add it to server startup. Also remember that you should use key authentication to open SSH connection automatically.

Make backend Nginx listen only locally:

listen default;

Set up proxy box to pass all requests to backend:

upstream backend {
server {
    listen 80;
    server_name 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_pass http://backend;

Also add virtual host or location block for static content on proxy box.

For static file synchronisation, create rsync.sh, make it executable and add to crontab.

echo "Completed at: `/bin/date`" >> $LOGFILE

These are just the basic steps but they should give you an idea how to use this.

For example, I use Nginx proxying to make server changes unnoticeable. Some Bittorrent trackers use this method to hide their servers and come back sooner after their proxy servers are taken down.

Depending on the site you are hosting, every setup can be different, so use your imagination to make it suit your needs.

Join the Conversation


  1. Thanks for the write-up, I like the ssh tunnel suggestion. But won’t the backend server IP addresses be visible in netstat, even if they are not present in any configuration file?

    1. This assumes that your server doesn’t get compromised, just disabled/disconnected by the service provider.
      This probably would not work in some cases. šŸ™‚

Leave a comment

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