如何知道何时发送304 Not Modified响应

时间:2008-08-07 07:54:37

标签: language-agnostic http

我正在编写一种资源处理方法,用于控制对各种文件的访问,我希望能够使用浏览器的缓存。我的问题有两方面:

  1. 我需要检查哪些最终的HTTP标头,以便确定是否应该发送304响应,以及在检查时我在寻找什么?

  2. 此外,当我最初发送文件(如“Last-Modified”)作为200响应时,是否需要发送任何标题?

  3. 一些伪代码可能是最有用的答案。


    缓存控制标头怎么样?可能的各种可能值会影响您发送给客户端的内容(即max-age),还是应该只有在被修改后才能遵守?

5 个答案:

答案 0 :(得分:8)

以下是我实施它的方式。代码已经工作了一年多,并且有多个浏览器,所以我认为它非常可靠。这基于RFC 2616并观察各种浏览器发送的内容和时间。

这是伪代码:

server_etag = gen_etag_for_this_file(myfile)
etag_from_browser = get_header("Etag")

if etag_from_browser does not exist:
    etag_from_browser = get_header("If-None-Match")
if the browser has quoted the etag:
    strip the quotes (e.g. "foo" --> foo)

set server_etag into http header

if etag_from_browser matches server_etag
    send 304 return code to browser

这是我处理此问题的服务器逻辑片段。

/* the client should set either Etag or If-None-Match */
/* some clients quote the parm, strip quotes if so    */
mketag(etag, &sb);

etagin = apr_table_get(r->headers_in, "Etag");
if (etagin == NULL)
    etagin = apr_table_get(r->headers_in, "If-None-Match");
if (etag != NULL && etag[0] == '"') {
    int sl; 
    sl = strlen(etag);
    memmove(etag, etag+1, sl+1);
    etag[sl-2] = 0;
    logit(2,"etag=:%s:",etag);
}   
... 
apr_table_add(r->headers_out, "ETag", etag);
... 
if (etagin != NULL && strcmp(etagin, etag) == 0) {
    /* if the etag matches, we return a 304 */
    rc = HTTP_NOT_MODIFIED;
}   

如果你想在etag生成方面提供一些帮助,可以发布另一个问题,我也会挖出一些代码来解决这个问题。 HTH!

答案 1 :(得分:4)

304 Not Modified响应可能来自带有If-Modified-Since(“IMS”)或If-Not-Match(“INM”)标头的GET或HEAD请求。

为了确定在收到这些标头时要做什么,想象一下你正在处理没有这些条件标头的GET请求。确定您的ETag和Last-Modified标头的值将在该响应中,并使用它们来做出决定。希望您构建了系统,以确定这一点比构建完整响应的成本更低。

如果有INM并且该标题的值与您在ETag中放置的值相同,则以304响应。

如果存在IMS且该标头中的日期值晚于您在Last-Modified中放置的日期值,则以304响应。

否则,请继续进行,就好像请求不包含这些标题一样。

对于问题第2部分的最小努力方法,找出可以在Web应用程序中轻松正确生成哪些(Expires,ETag和Last-Modified)标头。

建议的阅读材料:

http://www.w3.org/Protocols/rfc2616/rfc2616.html

http://www.mnot.net/cache_docs/

答案 2 :(得分:3)

如果客户端已明确声明其页面可能已在其缓存中,则应发送304。这称为条件GET,应在请求中包含 if-modified-since 标头。

基本上,此请求标头包含客户端声称拥有缓存副本的日期。您应该检查此日期之后内容是否已更改,如果没有,则发送304。

有关RFC中的相关部分,请参阅http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25

答案 3 :(得分:2)

我们还处理缓存但安全的资源。如果您发送/生成ETAg标头(RFC 2616第13.3节建议您应该),那么客户端必须在条件请求中使用它(通常在If-None-Match - HTTP_IF_NONE_MATCH - 标头中)。如果你发送Last-Modified标题(你应该再次),那么你应该检查If-Modified-Since - HTTP_IF_MODIFIED_SINCE - 标题。如果你发送两个,那么客户端应该发送两个,但它必须发送ETag。另请注意,验证仅定义为检查条件标头是否与您要发送的条件标头严格相等。此外,只有强验证器(如ETag)才会用于远程请求(只请求部分资源)。

在实践中,由于我们所保护的资源是相当静态的,并且可以接受一秒的滞后时间,我们正在做以下事情:

  1. 检查用户是否有权访问所请求的资源

    如果不是,请根据需要重定向或发送4xx响应。我们将针对看起来像黑客企图或公然尝试执行安全性结束运行的请求生成404响应。

  2. 将If-Modified-Since标头与Last-Modified标头进行比较,我们将发送(见下文)严格相等

    如果匹配,则发送304 Not Modified响应并退出页面处理

  3. 使用所请求资源的修改时间

    创建Last-Modified标头

    在RFC 2616中查找HTTP日期格式

  4. 发送标题和资源内容以及适当的Content-Type

  5. 我们决定避开ETag标题,因为它对于我们的目的来说是过度的。我想我们也可以使用日期时间戳作为ETag。如果我们转移到一个真正的ETag系统,我们可能会存储资源的计算哈希值,并将它们用作ETag。

    如果您的资源是动态生成的,例如数据库内容,那么ETag可能会更好地满足您的需求,因为它们只是您认为合适的文本。

答案 4 :(得分:1)

关于缓存控制:

除了将其设置为合理的值之外,您在服务时不必担心缓存控制。它基本上告诉浏览器和其他下游实体(例如代理)在超时缓存之前应该经过的最长时间。