Skip to content

Commit

Permalink
Set proper http cache headers
Browse files Browse the repository at this point in the history
If we want to be able to use CDNs we need to set public
cache headers and a expiration duration.

The expiration defaults to 3 hours but you can set a

    ALCHEMY_JSON_API_CACHE_DURATION

variable to any integer.
  • Loading branch information
tvdeyen committed Sep 10, 2021
1 parent 0706849 commit eb8b7ce
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 5 deletions.
12 changes: 12 additions & 0 deletions app/controllers/alchemy/json_api/pages_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,29 @@ def index
end
end
end

expires_in cache_duration, caching_options.merge(public: @pages.none?(&:restricted?))
end

def show
if stale?(last_modified: last_modified_for(@page), etag: @page.cache_key)
# Only load page with all includes when browser cache is stale
render jsonapi: api_page(load_page)
end

expires_in cache_duration, caching_options.merge(public: !@page.restricted?)
end

private

def cache_duration
ENV.fetch("ALCHEMY_JSON_API_CACHE_DURATION", 3).to_i.hours
end

def caching_options
{ must_revalidate: true }
end

# Get page w/o includes to get cache key
def load_page_for_cache_key
@page = page_scope.where(id: params[:path]).
Expand Down
42 changes: 37 additions & 5 deletions spec/requests/alchemy/json_api/pages_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,27 @@
)
end

it "sets cache headers" do
it "sets public cache headers" do
get alchemy_json_api.page_path(page)
expect(response.headers["Last-Modified"]).to eq(page.published_at.utc.httpdate)
expect(response.headers["ETag"]).to match(/W\/".+"/)
expect(response.headers["Cache-Control"]).to eq("max-age=0, private, must-revalidate")
expect(response.headers["Cache-Control"]).to eq("max-age=10800, public, must-revalidate")
end

context "if page is restricted" do
let(:page) do
FactoryBot.create(
:alchemy_page,
:public,
:restricted,
published_at: DateTime.yesterday,
)
end

it "sets private cache headers" do
get alchemy_json_api.page_path(page)
expect(response.headers["Cache-Control"]).to eq("max-age=10800, private, must-revalidate")
end
end

context "if browser sends fresh cache headers" do
Expand Down Expand Up @@ -137,11 +153,27 @@
context "as anonymous user" do
let!(:pages) { [public_page] }

it "sets cache headers of latest published page" do
it "sets public cache headers of latest published page" do
get alchemy_json_api.pages_path
expect(response.headers["Last-Modified"]).to eq(pages.max_by(&:published_at).published_at.utc.httpdate)
expect(response.headers["ETag"]).to match(/W\/".+"/)
expect(response.headers["Cache-Control"]).to eq("max-age=0, private, must-revalidate")
expect(response.headers["Cache-Control"]).to eq("max-age=10800, public, must-revalidate")
end

context "if one page is restricted" do
let!(:restricted_page) do
FactoryBot.create(
:alchemy_page,
:public,
:restricted,
published_at: DateTime.yesterday,
)
end

it "sets private cache headers" do
get alchemy_json_api.pages_path
expect(response.headers["Cache-Control"]).to eq("max-age=10800, private, must-revalidate")
end
end

context "if browser sends fresh cache headers" do
Expand Down Expand Up @@ -200,7 +232,7 @@
get alchemy_json_api.pages_path(filter: { page_layout_eq: "news" })
expect(response.headers["Last-Modified"]).to eq(news_page2.published_at.utc.httpdate)
expect(response.headers["ETag"]).to match(/W\/".+"/)
expect(response.headers["Cache-Control"]).to eq("max-age=0, private, must-revalidate")
expect(response.headers["Cache-Control"]).to eq("max-age=10800, public, must-revalidate")
end
end

Expand Down

0 comments on commit eb8b7ce

Please sign in to comment.