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

last updated in Categories , , , , , , , , ,

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

        =============                               |---- apache1 (
        | ISP Router|                               |
        =============                               |---- apache2 (
             |                                      |
             |                                      |---- db1 (
             |      |eth0 -> ----------/
             |-lb0==|                          /
             |      |eth1 ->
             |      |eth0 -> ----------\
             |-lb1==|                          /    |---- apache1 (
                    |eth1 ->     |
                                                    |---- apache2 (
                                                    |---- db1 (
    • 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.
    • 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 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 -out
    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 as I’m going to use

    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
    # openssl rsa -in -out

    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 -signkey -out
    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 ###
    	ssl 		on;
    	### SSL log files ###
            access_log      logs/ssl-access.log;
            error_log       logs/ssl-error.log;
    	### SSL cert files ###
            ssl_certificate      ssl/;
            ssl_certificate_key  ssl/;
    	### 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
    Or better use the following command:
    $ openssl s_client -connect -CApath /usr/share/ssl-cert/ -servername

    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


    Posted by: Vivek Gite

    The author is the creator of nixCraft and a seasoned sysadmin, DevOps engineer, and a trainer for the Linux operating system/Unix shell scripting. Get the latest tutorials on SysAdmin, Linux/Unix and open source topics via RSS/XML feed or weekly email newsletter.

    17 comment

    1. 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 ?

    2. 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:

      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,


    3. 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?

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

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

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

         proxy_pass  http://nixcraft;
    4. 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



    5. 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!

    6. 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?


    7. 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.

      1. 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?


    8. 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 ?

    9. 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?

    10. 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.

        Still, have a question? Get help on our forum!