Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Being served a response directly from the cache when clicking back button

Note that I don't think this problem is related to Backbone or JavaScript but it is necessary to include some Backbone code as context on the issue.

The Related Code

I have a client-side Backbone router with a route which takes a parameter called contactId. It looks similar to this:

Backbone.Router.extend({

  routes: {
    "jobs/new?contact_id=:contactId": "newForContact"
  },

  // Fetch the contact and initialize a new job model which 
  // is associated with that contact.
  newForContact: function(contactId) {
    var contact = new Contact(id: contactId);
    contact.fetch({
      success: _.bind(function(model, resp) {
        var job = new Job(contact: contact);
        this.new(job);
      }
    }, this));
  },

  // Show the JobView for the given job.
  new: function(jobModel) {
    view = new JobView(job: jobModel);
    $('body').append(view.render().el);
  }
};

Now I'm trying to use this setup with pushState turned on.

When I hit the route which triggers the newForContact route, everything works as expected. However, if I press the browser's back button at this point, I am served the JSON response from the contact.fetch() method straight out of the browser's cache. No request is sent to the server.

fetch JSON response

The App Logs

You can see this in the Rails app logs. In this part, I visit the route which triggers newForContact.

Started GET "/jobs/new?contact%5Bid%5D=1&contact%5Btype%5D=Customer" for 127.0.0.1 at 2012-10-31 22:41:48 +0000
Processing by JobsController#new as HTML
  Parameters: {"contact"=>{"id"=>"1", "type"=>"Customer"}}
  User Load (0.3ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 1]]
  Business Load (0.3ms)  SELECT "businesses".* FROM "businesses" WHERE "businesses"."id" IN (1)
  Rendered shared/_search_form.html.erb (0.3ms)
  Job Load (0.4ms)  SELECT "jobs".* FROM "jobs" WHERE "jobs"."business_id" = 1 ORDER BY created_at desc
  Rendered jobs/_list.html.erb (1.5ms)
  Rendered jobs/index.html.erb within layouts/application (4.1ms)
  Rendered layouts/_head_content.html.erb (0.7ms)
  Rendered layouts/_flash.html.erb (0.0ms)
Cache read: views/jobs/main_nav/d6a805d9b6f285e424f207add4f35595
Read fragment views/jobs/main_nav/d6a805d9b6f285e424f207add4f35595 (0.4ms)
  Rendered layouts/_nav.html.erb (0.6ms)
  Rendered layouts/_header.html.erb (0.7ms)
Completed 200 OK in 12ms (Views: 8.0ms | ActiveRecord: 1.0ms)
Cache read: http://print.dev/customers/1?

You can see that it fetches the contact at this point with a JSON request.

Started GET "/customers/1" for 127.0.0.1 at 2012-10-31 22:41:48 +0000
Processing by CustomersController#show as JSON
  Parameters: {"id"=>"1"}
  User Load (0.5ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 1]]
  Business Load (0.4ms)  SELECT "businesses".* FROM "businesses" WHERE "businesses"."id" IN (1)
  Customer Load (0.2ms)  SELECT "customers".* FROM "customers" WHERE "customers"."business_id" = 1 AND "customers"."id" = $1 LIMIT 1  [["id", "1"]]
  CustomerEmployee Load (0.3ms)  SELECT "customer_employees".* FROM "customer_employees" WHERE "customer_employees"."employer_id" IN (1)
  Job Load (0.4ms)  SELECT "jobs".* FROM "jobs" WHERE "jobs"."contact_type" = 'Customer' AND "jobs"."contact_id" IN (1)
  Invoice Load (0.3ms)  SELECT "invoices".* FROM "invoices" WHERE "invoices"."client_id" IN (1)
  Job Load (0.5ms)  SELECT "jobs".* FROM "jobs" WHERE "jobs"."contact_id" = 1 AND "jobs"."contact_type" = 'Customer' AND "jobs"."state" = 'finished' AND "jobs"."invoice_id" IS NULL
  Rendered customers/show.json.rabl (2.8ms)
Completed 200 OK in 67ms (Views: 3.2ms | ActiveRecord: 2.5ms)

At this point, I would press the browser's back button but no new request is logged at the server.

Rails Environments

This only happens on my staging server (Heroku), not in development. I can recreate it locally by running the app with Pow in the staging environment which has caching turned on in the rails configs.

config.action_controller.perform_caching = true

Note that even with caching turned on, I can't recreate the bug in the development environment.

This issue occurs in Chrome 22.0.1229.94, FF 16.0.2 and Safari 6.0.1. I'm using Rails 3.2.8.

Possibly Related Questions

It seems like this guy was having a very similar problem to me.

Live Sample

If you really want to you can view the problem live on my staging server on Heroku.

Steps to Repro (edit: these don't work any longer since I patched the problem).

  1. Log in here with email: [email protected] and pass: foobar
  2. Visit http://print-staging.herokuapp.com/customers/2. You should see a messed up looking dialog box open up.
  3. Click the small "New job" link in the dialog. The page should change and a new dialog should open.
  4. Click the browser back button.
like image 303
David Tuite Avatar asked Oct 29 '12 21:10

David Tuite


People also ask

What is backward forward cache?

Back/forward cache (or bfcache) is a browser optimization that enables instant back and forward navigation. It significantly improves the browsing experience for users—especially those with slower networks or devices.


1 Answers

You can add a no-cache header to your response on the server-side, this should instruct the browser to not cache the response:

response.headers["Cache-Control"] = "no-cache"
like image 114
kabaros Avatar answered Sep 28 '22 05:09

kabaros