Now that nginx+pagespeed makes a lot of automated optimizations, we would like to offload the task of serving assets that do not change often to a content delivery network service, and I'll demonstrate the trick with Cloudfront.
We'll gain the following:
- Offload our webserver by serving static assets.
- Serve assets from a different, cookieless, domain.
- Serve the assets from a geographically closer datacenter.
Note that as Cloudfront will need to have an HTTP access to your webserver, you won't be able to test the content delivery locally.
Our target architecture, and in green, the parts we focus on in this article:
Create a Cloudfront distribution
Cloudfront is a paying service from Amazon Web Services. I find it pretty unexpensive for what it does, but you should probably pay attention to the pricing before trying out stuff in this section.
Also, I consider that you're already familiar with Amazon Web Services, and that you already have an account that you can use for this purpose.
An HTTP endpoint relaying assets from another server is called a distribution in Amazon Cloudfront.
Let's create one, and tune a few settings (you can also edit everything after the distribution is created).
- Login to your AWS console, and create a new (web) distribution that will be used as your website's CDN.
- Setup your web application FQDN
- Origin Domain Name: you're publicly accessible FQDN
- Whitelist Origin HTTP header (will help with CORS)
- Forward Headers: Whitelist
- Whitelist Headers: add Origin
Click «Create distribution», grab your distribution's domain name (for me, d13rn6zvbpb4kh.cloudfront.net) and let's go back to nginx configuration while Amazon is spreading this distribution configuration to the various availability zones you selected.
You can also grab a coffee (not required).
Configure nginx+pagespeed to rewrite assets' URLs to use our CDN distribution
Yet another great thing about nginx+pagespeed is that a simple configuration option enables rewriting all assets URLs that are relative to a given domain to another. Which means you can, without changing anything to your backend service code, change all the image, scripts, styles URL so it hits the CDN.
Just adapt and include the two following lines in the nginx cofiguration's server section that is relevant to your website.
pagespeed Domain http://ngxps.rdc.li/; pagespeed MapRewriteDomain https://d13rn6zvbpb4kh.cloudfront.net http://ngxps.rdc.li;
I used the HTTPS version of Cloudfront distribution here, because it makes it easier to transition your website from HTTP to HTTPS, as browsers will refuse HTTP assets if you're serving over SSL. The oposite being false, it won't harm, but feel free to use the HTTP distribution if you do prefer it.
Of course, you can use the same technique with another CDN, but this is out of this article scope, even if the pagespeed configuration will look the same.
You'll need a publicly accessible HTTP endpoint to test your newly setup, backed by Cloudfront CDN. Just launch both containers in your favorite scheduler (for example, using a Kubernetes Deployment), and you should be set.
As I can't cover all the specific aspects of running it on a (probably not alone) given production site, I won't get in details here but if you don't use a scheduler, just fire up the two containers as you would have done locally and notice the rewrites.
Multiple nginx frontends can lead to get the CDN lost, as the hash won't be the same. Currently working on this, as it can be usefull for example to have a round robin dns lead to more than one front http server, or if you're using something like kubernetes, having more than one pod working for the service connected to your ingress.
You may hit cross-domain assets failure:
Font from origin 'https://d13rn6zvbpb4kh.cloudfront.net' has been blocked from loading by Cross-Origin Resource Sharing policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://ngxps.rdc.li' is therefore not allowed access.».
A few solutions exist for this, but the easiest one (and probably not the most secure) is to add a rather permissive header to all responses sent by nginx:
# Cross domain insecure header add_header "Access-Control-Allow-Origin" "*";
Still not working? Keep in mind your assets are served from your webserver to Amazon Cloudfront, to the user, and the CDN-in-the-middle will cache not only the content but also the request headers. You should thus create a cache invalidation within your Cloudfront distribution to tell Amazon to refresh its informations (and wait a bit, as the invalidation will take a few minutes to propagate through the availability zones).