Sunday, 9 June 2019

Hosting a static site with minio and nginx


Hosting a static site is supper simple with aws however you lose some of the customisability of URLs and redirects. By using minio object store and nginx as a proxy you get back all the control over your redirects and URLs.

This arterial won't go into how to install or set up minio and nginx, because there are many ways to set them up. If you want to have a play docker is a grate way to get up and running quickly. For the config I will be setting up a site with the domain of example.com and a minio bucket name of my-bucket. I also have a minio service set up on static.example.com

The first notable thing about the config is removing the minio headers that have been set.

proxy_hide_header x-amz-request-id;
proxy_hide_header x-minio-deployment-id;

Next is the main location definition. The first bit I have in my location / is to rewrite the root to the index.html in the bucket.

rewrite ^/$ /my-bucket/index.html break;

I found that there was directory index functionality in minio. To get around this I set up a rewrite for any URL ending in / to /index.html. This then just displayed a page rather than minio's directory index. I still needed to do the main rewrite because when you first come to the website there will be know /

rewrite (.*)/$ /$1/index.html;

To stop duplicate content on /about/ and /about I set a 301 permanent redirect from /about to /about/

rewrite ^([^.]*[^/])$ /$1/ permanent;

Not found 404 pages have been a big thing for me lately. After playing with SPA's and not really finding a solution I'm happy with, return a 404 status code from the server was a must for the static site. I must say that the solution was a lot easier than with an SPA and was achieved in three steps. The first was to set a custom 404 page to 404.html that have created in my site.

location /error-404 {
  proxy_pass https://static.example.com/my-bucket/404.html;
}

Next was to make nginx handle any errors that the proxy throws, like not being able to find a file.

proxy_intercept_errors on;

Finally, to handle the error the proxy would have just thrown at the end of the location section I set a custom 404 error page for nginx.

error_page 404 /error-404;

The last bit of the config sets some caching headers for the assets directory.

expires 100d;
add_header Pragma public;
add_header Cache-Control "public";

Full Config

This is the full config for example.com

server {
  server_name example.com;

  listen [::]:80;
  listen 80;

  gzip on;
  gzip_disable "msie6";

  gzip_vary on;
  gzip_proxied any;
  gzip_comp_level 6;
  gzip_buffers 16 8k;
  gzip_types text/plain text/html text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;

  proxy_buffering off;

  proxy_intercept_errors on;

  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto $scheme;

  proxy_hide_header x-amz-request-id;
  proxy_hide_header x-minio-deployment-id;

  location / {
    #
    # The main redirect to the bucket
    #
    rewrite ^/$ /my-bucket/index.html break;

    #
    # Redirect urls ending with `/` to index.html so the page serves
    #
    rewrite (.*)/$ /$1/index.html;

    #
    # Redirect url not ending with a `/` to /index.html
    #
    rewrite ^([^.]*[^/])$ /$1/ permanent;

    #
    # Set up the proxy note the trailing `/` this is important
    #
    proxy_pass https://static.example.com/my-bucket;

    error_page 404 /error-404;
  }

  location /assets/ {
    expires 100d;
    add_header Pragma public;
    add_header Cache-Control "public";

    #
    # The main redirect to the bucket
    #
    rewrite ^/$ /my-bucket/index.html break;

    #
    # Redirect urls ending with `/` to index.html so the page serves
    #
    rewrite (.*)/$ /$1/index.html;

    #
    # Redirect url not ending with a `/` to /index.html
    #
    rewrite ^([^.]*[^/])$ /$1/index.html;

    #
    # Set up the proxy note the trailing `/` this is important
    #
    proxy_pass https://static.example.com/my-bucket/assets/;

    error_page 404 /error-404;
  }

  location /error-404 {
    proxy_pass https://static.example.com/my-bucket/404.html;
  }

}