htmx
htmx copied to clipboard
hx-boost breaks site when re-opning closed tab (command-shift-T)
When reopening a closed tab (command-shift-t
on Chrome) with hx-target
set to an element different than the body
, the page loads only the partial content returned from the server. Is this intended behavior?
I can confirm this issue. I don't think it is intended.
Problem is described at stackoverflow: https://stackoverflow.com/questions/9445588/re-open-last-closed-tab-causing-to-show-last-ajax-request-content
After restoring tab browser shows last cached ajax response.
You can use Cache-Control headers at server side to disable caching for Hx-Boosted requests. It will solve this issue but it's not elegant solution.
I belive it can be solved at the htmx side by adding timestamp to ajax requests to avoid caching.
<div hx-boost="true" hx-target="#main-content">
<a href="/sample" >
<span>Sample page</span>
</a>
</div>
If htmx could make request to /sample?cache-busting=1641770788510
instead of /sample
and then push url ```/sample`` to history, I think this would solve this issue.
After some rethink I thought about the downsides. We don't want completely disable cache. '''/sample? Hx-boosted=true''' would be better solution. Address is different so We don't break re-opening tab but We still have caching enabled for better user experience.
You can use the Vary-Header to distinguish HTMX requests from regular ones:
Vary: HX-Request
You can use the Vary-Header to distinguish HTMX requests from regular ones:
Vary: HX-Request
Oh that's clever, I'll try it as soon as I can!
@oliverhanappi @David-Guillot probably it won’t work. After reopening closed tab it will be returned from browser cache without hitting sever.
There are two solutions.
-
Do not cache fragments by sending correct cache headers from servers.
-
Modify htmx to add query param (for example ?fragment=true to url when requesting fragment. After that they will use different caches. There will be separate cache entry for fragment and full page. We can still benefit from caching fragments and closed tab restore behavior will be fixed.
in django adding no-cache headers when returning a fragment worked for me
if request.META.get("HTTP_HX_REQUEST", None):
response["Cache-Control"] = "no-cache, no-store, must-revalidate"
response["Pragma"] = "no-cache"
response["Expires"] = "0"
There are two solutions.
1. Do not cache fragments by sending correct cache headers from servers. 2. Modify htmx to add query param (for example ?fragment=true to url when requesting fragment. After that they will use different caches. There will be separate cache entry for fragment and full page. We can still benefit from caching fragments and closed tab restore behavior will be fixed.
- This works, either with
Cache-Control
/Pragma
/Expires
or withVary: Hx-Request
. The downside seems to implement this behavior in every htmx client library. - This would obviously work and have the upside of being a native solution for every htmx user. But maybe it has some downsides that we don't see.
@1cg what's your opinion on this? If you think solution 2 is the way to go, I'll open a PR here. Otherwise I'll open a PR on django-htmx
, but others will have to implement the fix in other libs.
The Vary
header from the server is "the right thing":
https://htmx.org/docs/#caching
But I am open to a change for boosted links where we submit a cache-busting parameter that isn't included in the URL pushed into the browser history as a pragmatic solution as well. That would certainly make things easier on htmx developers.
@David-Guillot any interest in looking into this? A quick look at the code indicates it won't be trivial, but not impossible either...
@1cg I'll give it a try. Have you seen Adam's arguments for not implementing systematic Vary: Hx-Request
on django-htmx's side (cf adamchainz/django-htmx#300)? It's interesting because it makes the htmx-side solution more powerful: the "reopen tab" issue will be fixed and fragment caching won't be hurt.
#1206
Okay everyone (especially @thekashifmalik and @kucharzyk), this issue can be closed, as 613f7b61d59e8ac4741e02d9f468ca635773ab2d (released with 1.8.5) implements a configurable (disabled by default) cache-busting queryparam.