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

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

    🐧 Get the latest tutorials on Linux, Open Source & DevOps via RSS feed or Weekly email newsletter.

    🐧 17 comments so far... add one

    CategoryList of Unix and Linux commands
    Disk space analyzersncdu pydf
    File Managementcat
    FirewallAlpine Awall CentOS 8 OpenSUSE RHEL 8 Ubuntu 16.04 Ubuntu 18.04 Ubuntu 20.04
    Network UtilitiesNetHogs dig host ip nmap
    OpenVPNCentOS 7 CentOS 8 Debian 10 Debian 8/9 Ubuntu 18.04 Ubuntu 20.04
    Package Managerapk apt
    Processes Managementbg chroot cron disown fg jobs killall kill pidof pstree pwdx time
    Searchinggrep whereis which
    User Informationgroups id lastcomm last lid/libuser-lid logname members users whoami who w
    WireGuard VPNAlpine CentOS 8 Debian 10 Firewall Ubuntu 20.04
    17 comments… add one
    • Nils Eriksen Jul 30, 2010 @ 7:12

      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 ?

    • Robert E. Lee Jan 10, 2011 @ 19:04

      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,


    • hanscees Jan 19, 2011 @ 21:45

      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?

      • 🐧 nixCraft Jan 19, 2011 @ 22:08

        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;
    • Axel Jan 24, 2012 @ 18:07

      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



    • Anderson Santos Mar 20, 2012 @ 6:06

      I also had to add ssl on directive.

      Thank you very much for the info

    • Cal Leeming Apr 18, 2012 @ 14:30

      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!

    • Neil Aug 13, 2012 @ 21:02

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



    • Aron Szekely Oct 26, 2012 @ 19:54

      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?


    • Ram Nov 15, 2012 @ 3:35

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

    • Visti Andresen Jan 12, 2013 @ 11:44

      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.

      • Szop Apr 22, 2013 @ 21:36

        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?


    • Sean Jul 11, 2013 @ 15:46

      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 ?

    • sm Aug 29, 2013 @ 3:09

      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?

    • Aegis Mar 24, 2014 @ 11:45

      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.

    • Tsingao Feb 20, 2015 @ 2:07

      You’re missing a closing curly bracket from the server { entry.

    • Toast Jan 2, 2016 @ 2:37

      Awesome tutorial! This helped me to get Nginx working as a TLS termination proxy on Windows.

    Leave a Reply

    Your email address will not be published.

    Use HTML <pre>...</pre> for code samples. Still have questions? Post it on our forum