lazr.restfulclient has the correct behavior because the lazr.restful installation used for tests doesn't serve a 'Vary' header. Launchpad does serve a 'Vary' header: "Cookie,Authorization,Accept", and this triggers what I think is a bug in httplib2. Here's the httplib2 code: 1057 vary_headers = vary.lower().replace(' ', '').split(',') 1058 for header in vary_headers: 1059 key = '-varied-%s' % header 1060 value = info[key] 1061 if headers.get(header, '') != value: 1062 cached_value = None 1063 break Let me run you through it. When we requested the service root the first time, Launchpad served a Vary header of "Cookie,Authorization,Accept". We cached that header along with the rest of the original response. Now we're about to request the service root again, and we would like to make it a conditional request. Here's what that Vary header is saying: "If you're about to request this URL, and you're thinking of making it conditional, but the value you're planning to send for 'Cookie' (or Authorization, or Accept) is different from what you cached the first time, don't even bother!" Why? Well, the simplest example is Accept. Let's say you cache the WADL representation of the service root, and then you're about to make a request for the JSON representation of the service root. Can you make that request conditional? No, because if the server returns 304 Not Modified, you don't have anything on the client side you can use. The cached WADL representation is not a valid substitute for a JSON representation. "Vary: Accept" means "Different media types are not interchangeable." (In real life I've hacked the cache used by httplib2 to store different representations of the same resource in different files, so this doesn't matter for our purposes. Before the hack, the WADL and JSON representations of a resource were constantly trading places in a single file because of this Vary header. This was the right thing to do in terms of HTTP, but it was useless from a caching perspective.) OK, now the thing I think is a bug in httplib2. Launchpad serves "Vary: Cookie", but (on the web service) it doesn't serve Cookie in its responses, and lazr.restfulclient never sends Cookie in its requests. So the "Vary: Cookie" thing shouldn't matter, right? Well, take another look at the code. 1059 key = '-varied-%s' % header 1060 value = info[key] 1061 if headers.get(header, '') != value: 1062 cached_value = None 1063 break info['-varied-cookie'] is None, because the server didn't send a Cookie header the first time. headers.get('cookie') is the empty string, because the client isn't sending a Cookie header this time. Since None != "", httplib2 decides that the cached representation is not a valid response to the current request, for reasons exactly analogous to "Different media types are not interchangeable." The conditional request isn't made, because if the condition succeeded there'd be no file on disk that could be used to fulfill the request. The problem is that info[key] and headers.get(header, '') have different default values. When a header is present neither in the cached response nor in the incoming request, it doesn't "vary", and restrictions caused by the Vary header shouldn't apply. I need to look at the HTTP standard to see what Vary means when the header is not present at all, but I'm pretty sure this is a simple mistake in the Python. If I change the default value used in headers.get() to None, then I get the behavior I'd expect. Fixing this is a little tricky since it's httplib2 code that can't easily be overridden.