bigskysoftware / htmx

</> htmx - high power tools for HTML
https://htmx.org
Other
37.12k stars 1.25k forks source link

hx-boost breaks site when re-opning closed tab (command-shift-T) #724

Open thekashifmalik opened 2 years ago

thekashifmalik commented 2 years ago

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?

kucharzyk commented 2 years ago

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.

kucharzyk commented 2 years ago

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.

oliverhanappi commented 2 years ago

You can use the Vary-Header to distinguish HTMX requests from regular ones:

Vary: HX-Request
David-Guillot commented 2 years ago

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!

kucharzyk commented 2 years ago

@oliverhanappi @David-Guillot probably it won’t work. After reopening closed tab it will be returned from browser cache without hitting sever.

kucharzyk commented 2 years ago

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.

jakewaxman commented 2 years ago

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"
David-Guillot commented 1 year ago

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.
  1. This works, either with Cache-Control/Pragma/Expires or with Vary: Hx-Request. The downside seems to implement this behavior in every htmx client library.
  2. 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.

1cg commented 1 year ago

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...

David-Guillot commented 1 year ago

@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.

David-Guillot commented 1 year ago

1206

David-Guillot commented 1 year ago

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.