Nginx, proxy passing to Apache, and SSL

apache-2.2nginxreverse-proxyssl

I have Nginx and Apache set up with Nginx proxy-passing everything to Apache except static resources. I have a server set up for port 80 like so:

server {
    listen 80;
    server_name *.example1.com *.example2.com;

    [...]

    location ~* \.(?:ico|css|js|gif|jpe?g|png|pdf|te?xt)$ {
        access_log off;
        expires max;
        add_header Pragma public;
        add_header Cache-Control "public, must-revalidate, proxy-revalidate";
        add_header Vary: Accept-Encoding;
    }

    location / {
        proxy_pass      http://127.0.0.1:8080;
        include /etc/nginx/conf.d/proxy.conf;
    }
}

And since we have multiple ssl sites (with different ssl certificates) I have a server{} block for each of them like so:

server {
    listen 443 ssl;
    server_name *.example1.com;

    [...]

    location ~* \.(?:ico|css|js|gif|jpe?g|png|pdf|te?xt)$ {
        access_log off;
        expires max;
        add_header Pragma public;
        add_header Cache-Control "public, must-revalidate, proxy-revalidate";
        add_header Vary: Accept-Encoding;
    }

    location / {
        proxy_pass      https://127.0.0.1:8443;
        include /etc/nginx/conf.d/proxy.conf;
        proxy_set_header X-Forwarded-Port 443;
        proxy_set_header X-Forwarded-Proto https;
    }
}

server {
    listen 443 ssl;
    server_name *.example2.com;

    [...]

    location ~* \.(?:ico|css|js|gif|jpe?g|png|pdf|te?xt)$ {
        access_log off;
        expires max;
        add_header Pragma public;
        add_header Cache-Control "public, must-revalidate, proxy-revalidate";
        add_header Vary: Accept-Encoding;
    }

    location / {
        proxy_pass      https://127.0.0.1:8445;
        include /etc/nginx/conf.d/proxy.conf;
        proxy_set_header X-Forwarded-Port 443;
        proxy_set_header X-Forwarded-Proto https;
    }
}

First of all, I think there is a very obvious problem here, which is that I'm double-encrypting everything, first at the nginx level and then again by Apache. To make everything worse, I just started using Amazon's Elastic Load Balancer, so I added the certificate to the ELB and now SSL encryption is happening three times. That's gotta be horrible for performance.

What is the sane way to handle this? Should I be forwarding https on the ELB -> http on nginx -> http on apache?

Secondly, there is so much duplication above. Is the best method to not repeat myself to put all of the static asset handling in an include file and just include it in the server?

Best Answer

Part 1: passing along encrypted traffic

It's fine to have only your first server "terminate" the SSL form the client connection. You should have a setup where the machines that are controlled by you can exchange traffic in a trusted manner (e.g. on a private network) and not have to run SSL all the time again and again. SSL is really only for transport security. Browser -> first webserver is an untrusted transport. Webserver -> internal services should be trusted, as you will also communicate with e.g. file servers, database servers, and maybe even others like memcache, redis, and so on.

Part 2: nginx config duplication

nginx' include statement allows for a very liberal form of reuse and I can see that you already started using it - keep going and refactor that config until it looks "right". Also, nginx' variable expansion is pretty useful to make nice reusable includes without leaving a mess.