nginx: Setup SSL Reverse Proxy (Load Balanced SSL Proxy)

by on April 1, 2010 · 15 comments· LAST UPDATED May 19, 2013

in , ,

A reverse proxy is a proxy server that is installed in a server network. Typically, reverse proxies are used in front of Web servers such as Apache, IIS, and Lighttpd. How do I setup nginx web server as SSL reverse proxy?

When you've multiple backend web servers, encryption / SSL acceleration can be done by a reverse proxy. Nginx can act as SSL acceleration software. It provided the following benefits:

  • Easy of use : Nginx is easy to setup and upgrade.
  • Security : Nginx provide an additional layer of defense as Apache is behind the proxy. It can protect against common web-based attacks too.
  • Load Distribution : nginx use very little memory and can distribute the load to several Apache servers. It can even rewrite urls on fly.
  • Caching : Nginx act as a reverse proxy which offload the Web servers by caching static content, such as images, css, js, static html pages and much more.
  • Compression : Nginx can optimize and compress the content to speed up the load time.
  • Our Sample Setup

    Internet--
             |
        =============                               |---- apache1 (192.168.1.15)
        | ISP Router|                               |
        =============                               |---- apache2 (192.168.1.16)
             |                                      |
             |                                      |---- db1 (192.168.1.17)
             |      |eth0 -> 192.168.1.11 ----------/
             |-lb0==|                          /
             |      |eth1 -> 202.54.1.1:443---/
             |
             |      |eth0 -> 192.168.1.10 ----------\
             |-lb1==|                          /    |---- apache1 (192.168.1.15)
                    |eth1 -> 202.54.1.1:443---/     |
                                                    |---- apache2 (192.168.1.16)
                                                    |
                                                    |---- db1 (192.168.1.17)
    
    • lb0 - Linux box directly connected to the Internet via eth1. This is master SSL load balancer.
    • lb1 - Linux box directly connected to the Internet via eth1. This is backup SSL load balancer. This will become active if master networking failed.
    • 202.54.1.1 A virtual IP address that moves between lb0 and lb1. It is managed by keepalived.
    • nginx - It is installed on lb0 and lb1.
    • SSL Certificate - You need to install ssl certificates on lb0 and lb1.

    For demonstration purpose I'm going to use Self-signed SSL certificate, but you can use real SSL certificate signed by CAs.

    +------+	+-------------+	       +-------------------+
    |Client|  <---> |SSL-Nginx:443|	<----> |Apache-HTTP_mode:80|
    +------+        +-------------+        +-------------------+
    
    • You've the SSL connection between client and Nginx.
    • Then Nginx act as proxy server and makes unencrypted connection to Apache at port 80.
    • Nginx can cache all static file and other files.

    Generating Self-signed Certificate

    First, create required directories:
    # cd /usr/local/nginx/conf
    # mkdir ssl
    # cd ssl

    To create a private key, enter:
    # openssl genrsa -des3 -out nixcraft.in.key 1024
    Sample outputs:

    Fig.01: OpenSSL - Create a Private Key

    Fig.01: OpenSSL - Create a Private Key


    To create a CSR (Certificate Signing Request):
    # openssl req -new -key nixcraft.in.key -out nixcraft.in.csr
    Sample outputs:
    Fig.02: OpenSSL - Create a CSR (Certificate Signing Request)

    Fig.02: OpenSSL - Create a CSR (Certificate Signing Request)


    Please enter your domain name that you want to associate with the certificate. For example, for the Command Name I entered nixcraft.in as I'm going to use https://nixcraft.in/.

    How Do I Remove The Passphrase? (Optional)

    You can remove the passphrase so nginx can start on boot without entering the passphrase. Type the following commands
    # cp nixcraft.in.key nixcraft.in.key.bak
    # openssl rsa -in nixcraft.in.key.bak -out nixcraft.in.key

    Finally, you should see three files as follows (note I've created all files as vivek user and than moved lb0 and lb1 server /usr/local/ngnix/conf/ssl/ directory):
    # ls -l
    Sample outputs:

    Fig.03: All the files in ssl directory

    Fig.03: All the files in ssl directory


    # openssl x509 -req -days 365 -in nixcraft.in.csr -signkey nixcraft.in.key -out nixcraft.in.crt
    Sample outputs:
    Fig.04: Generating The Actual Self-signed  SSL Certificate

    Fig.04: Generating The Actual Self-signed SSL Certificate

    How Do I Copy SSL Certificates Files To lb1?

    You need to copy those files to lb1, enter:
    # ssh root@lb1 mkdir /usr/local/ngnix/conf/ssl
    # rsync -av /usr/local/ngnix/conf/ssl/* root@lb1:/usr/local/ngnix/conf/ssl/

    Configure Nginx As SSL Reverse Proxy (lb0 and lb1)

    Edit nginx.conf, enter (you need to edit files on both lb0 and lb1):
    # vi /usr/local/ngnix/conf/nginx.conf
    Edit / append as follows:

     
    server {
    	### server port and name ###
            listen          202.54.1.1:443;
    	ssl 		on;
            server_name     nixcraft.in;
     
    	### SSL log files ###
            access_log      logs/ssl-access.log;
            error_log       logs/ssl-error.log;
     
    	### SSL cert files ###
            ssl_certificate      ssl/nixcraft.in.crt;
            ssl_certificate_key  ssl/nixcraft.in.key;
     
    	### Add SSL specific settings here ###
     
     
    	ssl_protocols        SSLv3 TLSv1 TLSv1.1 TLSv1.2;
    	ssl_ciphers RC4:HIGH:!aNULL:!MD5;
         	ssl_prefer_server_ciphers on;
         	keepalive_timeout    60;
    	ssl_session_cache    shared:SSL:10m;
         	ssl_session_timeout  10m;
     
    	### We want full access to SSL via backend ###
         	location / {
    	        proxy_pass  http://nixcraft;
     
    		### force timeouts if one of backend is died ##
            	proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
     
    		### Set headers ####
                    proxy_set_header        Accept-Encoding   "";
    	        proxy_set_header        Host            $host;
    	        proxy_set_header        X-Real-IP       $remote_addr;
    	        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
     
    		### Most PHP, Python, Rails, Java App can use this header ###
    		#proxy_set_header X-Forwarded-Proto https;##
    		#This is better##
    	        proxy_set_header        X-Forwarded-Proto $scheme;
    		add_header              Front-End-Https   on;
     
     
    		### By default we don't want to redirect it ####
    	        proxy_redirect     off;
          }
     

    Save and close the file. Reload nginx:
    # /usr/local/nginx/sbin/nginx -t
    # /usr/local/nginx/sbin/nginx -s reload

    Verify port is opened:
    # netstat -tulpn | grep :443

    How Do I Test And Debug SSL Certificates From The Shell Prompt?

    Use the openssl command as follows:
    $ openssl s_client -connect nixcraft.in:443
    Or better use the following command:
    $ openssl s_client -connect nixcraft.in:443 -CApath /usr/share/ssl-cert/ -servername nixcraft.in

    How Do I Cache Common Files?

    Edit nginx.conf and add as follows to cache common files:

    location ~* \.(jpg|png|gif|jpeg|css|js|mp3|wav|swf|mov|doc|pdf|xls|ppt|docx|pptx|xlsx)$ {
            proxy_buffering           on;
            proxy_cache_valid 200 120m;
            expires 864000;
    }

    Save and close the file. Reload nginx:
    # nginx -s reload

    TwitterFacebookGoogle+PDF versionFound an error/typo on this page? Help us!

    { 15 comments… read them below or add one }

    1 Nils Eriksen July 30, 2010 at 7:12 am

    Really great post!!!
    I am wondering if you also can implement a sem loadbalancing for multiple backends after you have terminated the SSL connection in the nginx ?

    Reply

    2 Robert E. Lee January 10, 2011 at 7:04 pm

    Is it possible to configure nginx as a reverse proxy that forwards to a squid proxy server?

    Currently I am using Squid as a forward proxy to another internal Squid proxy server:
    cache_peer internal_squid_proxy parent 8000 0 no-query
    cache_peer_access internal_system allow internal_squid_proxy https

    I’ve looked at the reverse proxy SSL write up here:
    http://www.cyberciti.biz/faq/howto-linux-unix-setup-nginx-ssl-proxy/

    That looks pretty close to what I want to do, except instead of reversing directly to a web server, I need to reverse to a forward proxy.

    Ie, something like:
    location / {
    proxy_pass http://internal_squid_proxy:8000;
    }

    Is there a cache_peer equivalent (instead of proxy_pass) for nginx?

    Thank you,

    Robert

    Reply

    3 hanscees January 19, 2011 at 9:45 pm

    Where do you tell ngix to balance between ther two backend apache servers?
    I see no ip-adresses?

    Is that via roundrobin dns or something. Please enlighten us?

    Reply

    4 nixCraft January 19, 2011 at 10:08 pm

    Read Install nginx As Reverse Proxy Load Balancer look for the following config:

    upstream nixcraft  {
          # apache backends using round robin with equal weight
          server 192.168.1.15 weight=10 max_fails=3 fail_timeout=40s;
          server 192.168.1.16 weight=10 max_fails=3 fail_timeout=40s;
          # only comes alive when above two fails
          server 192.168.1.23 weight=1 backup;
    }
    

    And it is matched with the following in server {….} section:

     proxy_pass  http://nixcraft;

    Reply

    5 Axel January 24, 2012 at 6:07 pm

    Hi Viket,

    thanks for the great explanation. I figured out that I also needed to add

    ssl on;

    within the server {} section of nginx.conf

    Cheers

    Axel

    Reply

    6 Anderson Santos March 20, 2012 at 6:06 am

    I also had to add ssl on directive.

    Thank you very much for the info

    Reply

    7 Cal Leeming April 18, 2012 at 2:30 pm

    Great article – I had to very quickly set up SSL offloading in nginx for a virtualbox test environment, and this article just saved me an hour of messing around. Many thanks!

    Reply

    8 Neil August 13, 2012 at 9:02 pm

    Great article! Where is http://nixcraft located? What is the ip address based from your info above?

    Thanks,

    Neil

    Reply

    9 Aron Szekely October 26, 2012 at 7:54 pm

    Great tutorial,

    One question, I did not figure that out from the post: do the apache servers behind the proxy need to have the ssl certificates also installed and set up and be running an ssl enabled site?

    I have a setup with nginx doing reverse proxy for several apache boxes behind it, but when I disable ssl on the apache boxes and have the proxy_pass directive go to regular http://xx.xx.xx:80 behind the proxy.

    But having ssl on both the proxy and the servers behind it, could that be problematic?

    Thanks!

    Reply

    10 Ram November 15, 2012 at 3:35 am

    great article – helped me configure my reverse proxy in minutes. Love it :-)

    Reply

    11 Visti Andresen January 12, 2013 at 11:44 am

    I would consider contacting the page serving Apache server using regular http on port 80 a potential security problem.
    If one of the Apache machines gets compromised the hacker could sniff sensitive information (passwords, credit card numbers, what not) from the other hosts.

    Of course if they all runs the same web application its less of a problem since all of them would probably soon be hacked anyway.

    But if you like me are about to run multiple different services from the same IP address over https this might be a concern.

    Since I’m about to host them on virtual KVM instances I could give them each there own network (seems like a lot of extra work for me).

    Or attempt to have the backends serve the pages over https, this will of course come at a performance penalty, but I expect low traffic so it will probably be ok.

    Note: I’m not even sure that sniffing packages is possible on the virtual networks, but when hosting potential “open shell services”, I’d rather play it safe.

    Reply

    12 Szop April 22, 2013 at 9:36 pm

    location ~* \.(jpg|png|gif|jpeg|css|js|mp3|wav|swf|mov|doc|pdf|xls|ppt|docx|pptx|xlsx)$ {
    proxy_buffering on;
    proxy_cache_valid 200 120m;
    expires 864000;
    }

    Guess you meant 86400 (24h), right?

    Cheers,
    Szop

    Reply

    13 Sean July 11, 2013 at 3:46 pm

    So we’ve been using this for a while and it’s worked great. We had to add two new sites to our setup recently and it works, sort of.

    The problem seems to be related to proxy_pass it takes that url and works but there is a login screen for the user to enter in and after they authenticate they’re supposed to goto another page, but that always fails.

    Is there a good way to just tell nginx to pass all traffic related to a specific location no matter what comes after http://servername:port/basedir ?

    Reply

    14 sm August 29, 2013 at 3:09 am

    Nice article, and thank you very much for posting. I have the proxy working fine with Node.JS, running on port 9200. Node, in turn talks to ElasticSearch on port 9200. I am getting some interesting behavior, where FireFox is detecting insecure content, and most of it is the elasticSearch JSON data.

    I guess I’m wondering if I need to setup port 9200 on Ngnix as well as 3000 in the location directive.

    Any thoughts on this?

    Reply

    15 Aegis March 24, 2014 at 11:45 am

    This solution´╝Ü
    You’ve the SSL connection between client and Nginx.
    Then Nginx act as proxy server and makes unencrypted connection to Apache at port 80.

    I want solution like this:
    You’ve the SSL connection between client and Nginx.
    Then Nginx act as proxy server and makes SSL connection to backend web server at port 443.

    Reply

    Leave a Comment

    Tagged as: , , , , , , , ,

    Previous Faq:

    Next Faq: