From 0d83106a215d27288d3db00fcf102dba41df94cf Mon Sep 17 00:00:00 2001 From: evazion Date: Wed, 16 Dec 2020 01:31:32 -0600 Subject: [PATCH] autocomplete: fix cache issue related to content negotiation. This is the scenario: * You type something in autocomplete, let's say 'touhou'. * Autocomplete calls /autocomplete?search[query]=touhou&search[type]=tag_query * The endpoint returns JSON, because the autocomplete call sets an `Accept: application/json` header requesting JSON. * Visit /autocomplete?search[query]=touhou&search[type]=tag_query in your browser. * Notice that the cached JSON response is incorrectly returned, not an HTML response like the browser requested. The problem is that the response type is chosen based on the Accept header, but the response didn't set the `Vary: Accept` header, so the browser doesn't know the response type can vary and so it incorrectly returns the cached response. This issue is partially fixed by Rails 6.1 ([1]), which properly sets the `Vary: Accept` header when the response depends on the Accept header. However, the next issue is that Cloudflare doesn't respect the Vary header at all ([2], [3]). Therefore we can't use the Accept header to pick the format, instead we have explicitly specify the format with /autocomplete.json. This is clearer and better for caching anyway. Using the `Vary: Accept` header reduces the cache hit rate, because the exact format of the Accept header varies across browsers, which fragments the cache. Whew. [1] https://github.com/rails/rails/pull.36213 [2] https://community.cloudflare.com/t/cloudflare-cdn-cache-to-support-http-vary-header/160802 [3] https://support.cloudflare.com/hc/en-us/articles/115003206852 [4] https://www.smashingmagazine.com/2017/11/understanding-vary-header/ --- app/javascript/src/javascripts/autocomplete.js.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/src/javascripts/autocomplete.js.erb b/app/javascript/src/javascripts/autocomplete.js.erb index de025cd66..4046ccc7d 100644 --- a/app/javascript/src/javascripts/autocomplete.js.erb +++ b/app/javascript/src/javascripts/autocomplete.js.erb @@ -201,7 +201,7 @@ Autocomplete.render_item = function(list, item) { }; Autocomplete.autocomplete_source = function(query, type) { - return $.getJSON("/autocomplete", { + return $.getJSON("/autocomplete.json", { "search[query]": query, "search[type]": type, "limit": Autocomplete.MAX_RESULTS