iBoyko - "sort of" a website

Configuring Apache 2 with Nginx Serving Static Files

An example of how to configure an Apache webserver with Nginx webserver serving static files.

Posted by Yakov Boyko on January 22, 2020

Apache 2 serving both dynamic and static files as a web server is a pretty typical and tried setup. It is basic and easy enough to implement. However, a need to server static files separely often arises.

For example, this may be useful because we may want to store static files on a separate domain in order to increase the performance of the site. That is, by having a different webserver that is dedicated strictly to static files, we can optimize that server in a way that would be impossible if it also needed to serve dynamic pages alongside the static files. This may also prevent unneeded overhead of cookies being sent with HTTP requests for static files, thereby reducing network traffic.

The documentation for Django web-framework specifically suggests two separate webservers for serving dynamic and static pages.

A typical way to do that is to serve dynamic pages using Apache and static files using Nginx. Particularly, one can employ the setup called Reverse Proxy in which the Nginx receives all requests for all resources and then decides what to do with each request.

If a request is for a static file, then the Nginx server will promptly deal with that task. If, however, the request is for a dynamic resource, the Nginx will pass the request to the Apache server which will in turn serve back the needed resource.

This may look as follows

ubuntu@ip-xxx-xx-xx-xx:~$ sudo netstat -tlpn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      13390/nginx: master 
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN      7136/apache2        
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      641/systemd-resolve 
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1038/sshd           
tcp        0      0 127.0.0.1:5432          0.0.0.0:*               LISTEN      6123/postgres       
tcp6       0      0 :::22                   :::*                    LISTEN      1038/sshd  

That is, all icoming TCP requests on default port 80 are received by the Nginx server listening on that port. If a request is for a static file, it serves it. Otherwise, it passes the request to the Apache, which is listening on port 8080 locally. This is the Reverse Proxy setup. In this setup the Nginx is the proxy server, which makes it look to the outside world as if it is doing all the work.

Here is a possible (actual, working) configuration for the Nginx server in such a setup

server {
     listen   *:80;
     server_name  www.name.org name.org;
     access_log  /var/log/nginx/name-access.log;
     location / {
      proxy_pass http://localhost:8080;
      proxy_redirect off;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     }

     location ~ /\.ht {
      deny all;
     }

     location /static/ {
       root /var/www/path/to/project;
     }

     location /media/ {
       root /var/www/path/to/project;
     }

     #location ~* ^.+\.(jpg|js|jpeg|png)$ {
      #root /some/where/on/your/disks;
     #}
    # put your static hosting config here.
    }

Similarly, the Apache configuration may look like this

<VirtualHost 127.0.0.1:8080>
    ServerName  www.name.org
    ServerAlias name.org

    WSGIDaemonProcess name display-name=%{GROUP} python-path=/var/www/path/to/project
    #WSGIApplicationGroup %{GLOBAL}
    WSGIProcessGroup name

    <Directory var/path/to/project>
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/error-name.log
    CustomLog ${APACHE_LOG_DIR}/access-name.log combined
    LogLevel info