Adding a cache layer to Heroku

As recommended in the docs I’m trying to add a caché layer to my heroku deployment

They have 2 add-ons to do this:

I’m using this repo to test the deployment → https://github.com/frontity-demos/my-frontity-project
So, this deployment in Heroku → https://mighty-badlands-59259.herokuapp.com/

Performance report in default deployment (no addons)

Performance report after adding Edge plugin

⬢  my-frontity-project  master ⦾ heroku addons:create edge:hobby
 ›   Warning: heroku update available from 7.25.0 to 7.39.3.
Creating edge:hobby on ⬢ mighty-badlands-59259... $5/month
Successfully configured https://d19mgjkbf05ovs.cloudfront.net
Created edge-graceful-46203 as EDGE_AWS_ACCESS_KEY_ID, EDGE_AWS_SECRET_ACCESS_KEY, EDGE_DISTRIBUTION_ID, EDGE_URL
Use heroku addons:docs edge to view documentation

I don’t see any improvement after adding this addon

Do you think there are some settings I have to do in order to take more advantage of this plugin? Can you help me verify this plugin is a good/bad option?

How did you configured Fastly? Do you need cache-control headers for that?

How is the server response time of both tests? Does it improve?

Ok, after reading more carefully the Edge documentation I think I understand it better.

By adding this addon you get automatically a new layer that will be in front of the heroku app. This “layer” is set in CloudFront and the URL of this CloudFront layer is provided by one of the environment variables set by Edge (EDGE_URL )

⬢  my-frontity-project  master ⦿ heroku config:get EDGE_URL
 ›   Warning: heroku update available from 7.25.0 to 7.39.3.
https://d19mgjkbf05ovs.cloudfront.net
⬢  my-frontity-project  master ⦿ curl -i https://d19mgjkbf05ovs.cloudfront.net
HTTP/2 200 
content-type: text/html; charset=utf-8
content-length: 370736
server: Cowboy
date: Wed, 22 Apr 2020 07:14:56 GMT
via: 1.1 vegur, 1.1 86b86f43445d5446c8b16910b2a9b8f9.cloudfront.net (CloudFront)
vary: Accept-Encoding
x-cache: Hit from cloudfront
x-amz-cf-pop: MAD50-C1
x-amz-cf-id: gzY-3p7EdR6AcW7RcYqAvnjYc3gawwgFMiZ8P7NoHbjGTLga17Lq1w==
age: 31

<!doctype html>
  <html lang="en">
     ...
  </html>

This is the URL that actually provide performance improvements…

The thing is that in a real project you won’t want using this URL for the final user so you have to configure a custom domain as explained in the Edge docs

But for testing purposes, I think it’s verified that adding this addon is a good solution for adding a CDN layer in a Heroku deployment of a Frontity project


To check the status of the addon you can do

⬢  my-frontity-project  master ⦿ heroku addons:open edge
 ›   Warning: heroku update available from 7.25.0 to 7.39.3.
Opening https://addons-sso.heroku.com/apps/7f2d2ec8-ae7b-4259-89d0-d7c3f0bc2c8b/addons/4d011408-61c1-48f9-a365-937896171450...

which will open the proper URL in the browser where you can see the activitity of this layer and configure a few things

Awesome, great work :slightly_smiling_face:

What options do you have?

The static assets should be set to immutable (or something like one year). It seems like they are not being cached yet:

And for the HTML response, does Cloudfront support state-while-revalidate? If not, I guess my recommendation would be to set the expire to 5 minutes or so, but I would explain that in the docs so people can set it themselves.

I’ve discovered this tool → httpstat
that allows you to view the server response times in an easy way

Response times from CloudFront layer (203ms)

⬢  my-frontity-project  master ⦿ httpstat https://d19mgjkbf05ovs.cloudfront.net/
Connected to 13.224.105.208:443 from 192.168.100.13:56803

HTTP/2 200
content-type: text/html; charset=utf-8
content-length: 370736
server: Cowboy
date: Wed, 22 Apr 2020 07:14:56 GMT
via: 1.1 vegur, 1.1 52523006e1ee5c08eea6e9267e18fabf.cloudfront.net (CloudFront)
vary: Accept-Encoding
x-cache: Hit from cloudfront
x-amz-cf-pop: MAD50-C1
x-amz-cf-id: tXMWCK2EkGtLAb-GGBpqIu-tpuiu-0UmDsw3EcYeBLs5ZGd9o_YsnQ==
age: 5997

Body stored in: /var/folders/zh/c0rwtffn1dq_nzr1fp8gz1zh0000gn/T/tmpLmaNrT

  DNS Lookup   TCP Connection   TLS Handshake   Server Processing   Content Transfer
[    30ms    |      20ms      |     51ms      |       26ms        |       76ms       ]
             |                |               |                   |                  |
    namelookup:30ms           |               |                   |                  |
                        connect:50ms          |                   |                  |
                                    pretransfer:101ms             |                  |
                                                      starttransfer:127ms            |
                                                                                 total:203ms

Response times directly from Heroku (2905ms)

⬢  my-frontity-project  master ⦿ httpstat https://mighty-badlands-59259.herokuapp.com/
Connected to 52.70.139.21:443 from 192.168.100.13:56917

HTTP/1.1 200 OK
Server: Cowboy
Connection: keep-alive
Content-Type: text/html; charset=utf-8
Content-Length: 370736
Date: Wed, 22 Apr 2020 08:55:22 GMT
Via: 1.1 vegur

Body stored in: /var/folders/zh/c0rwtffn1dq_nzr1fp8gz1zh0000gn/T/tmp7Se2wX

  DNS Lookup   TCP Connection   TLS Handshake   Server Processing   Content Transfer
[    69ms    |      268ms     |     446ms     |      1088ms       |      1034ms      ]
             |                |               |                   |                  |
    namelookup:69ms           |               |                   |                  |
                        connect:337ms         |                   |                  |
                                    pretransfer:783ms             |                  |
                                                      starttransfer:1871ms           |
                                                                                 total:2905ms

Taking this into account:

I think the problem with the static files is that there’s this header cache-control: max-age=0 telling CloudFront not to cache these files

Captura de pantalla 2020-04-22 a las 13.04.05

Do you know who is setting this header?

It seems CloudFront doesn’t support state-while-revalidate

Can we add custom headers to some responses?

For what I understand → Proof of concept on Pantheon + Deploy on Google Cloud Platform
this is something planned to do but still not available

Maybe the solution here could be creating a custom server uses server.js as middleware and proxy requests by adding the proper headers in the responses (but this adds complexity to the whole workflow so having a way to customize headers directly from frontity would be the way to go here)

And yes, this whole thing about Cache layers, headers involved and how to set them is something that needs to be explained very well as it is an important part of the Frontity Architecture.

I’m thinking about adding a glossary section like → https://www.gatsbyjs.org/docs/glossary/
to explain things like stale-while-revalidate

Some interesting resources regading this:

Yes, Frontity is not adding any cache-control headers as of today.

We didn’t add them because we thought about adding a @frontity/headers package, but it requires first the server extensibility and that is not ready yet.

I guess that we could add default cache-control headers until that is added… no-cache for the server and immutable for the static assets for the hostings like Heroku that don’t have a way to add them.

But, is there a way to configure those headers in Cloudfront for now? Do you know why is it caching the HTML but not caching the static assets? What configuration options does it give you (if any)?

I’m going to discard this way of adding a CDN for now, because:

Maybe configuring a CDN layer with a service like KeyCDN is a better option to start with

I think Fastly may be a good option too, because it is free up to $50 (as far as I know).

I still prefer KeyCDN though, because it works great with stale-while-revalidate. But for a medium/large site, state-while-revalidate is not really useful.

@juanma could you please remind me what do you need from me here? Some insight on how to configure KeyCDN?