NodeJS/express: 缓存和304状态码

125

当我用 express 制作的网站重新加载时,使用 Safari(而不是 Chrome)会出现空白页面,因为 NodeJS 服务器向我发送了 304 状态码。

如何解决这个问题?

当然,这也可能只是 Safari 的问题,但实际上它在所有其他网站上都很好地工作,所以它也必须是我的 NodeJS 服务器的问题。

为了生成页面,我使用 Jade 和 res.render

更新:似乎这个问题是因为 Safari 在重新加载时发送 'cache-control': 'max-age=0'

更新2:我现在有一个变通方法,但是否有更好的解决方案呢? 变通方法:

app.get('/:language(' + content.languageSelector + ')/:page', function (req, res)
{
    // Disable caching for content files
    res.header("Cache-Control", "no-cache, no-store, must-revalidate");
    res.header("Pragma", "no-cache");
    res.header("Expires", 0);

    // rendering stuff here…
}

更新 3: 目前完整的代码部分如下:

app.get('/:language(' + content.languageSelector + ')/:page', pageHandle);

function pageHandle (req, res)
{
    var language = req.params.language;
    var thisPage = content.getPage(req.params.page, language);

    if (thisPage)
    {
        // Disable caching for content files
        res.header("Cache-Control", "no-cache, no-store, must-revalidate");
        res.header("Pragma", "no-cache");
        res.header("Expires", 0);

        res.render(thisPage.file + '_' + language, {
            thisPage : thisPage,
            language: language,
            languages: content.languages,
            navigation: content.navigation,
            footerNavigation: content.footerNavigation,
            currentYear: new Date().getFullYear()
        });
    }
    else
    {
        error404Handling(req, res);
    }
}

4
304并不是问题。它只是意味着您的响应没有修改,您的浏览器转而使用缓存来获取资源。您能否发布出现异常情况的相关代码? - Akshat Jiwan Sharma
3
是的,实际上它并没有被修改,但是Safari在使用CMD+R(重新加载)时会清空缓存,而服务器只会说它没有改变。 - h345k34cr
空白页面与304状态码有什么关系?Node也会向其他浏览器发送304。 - user568109
3
这是相关的,因为在使用304时,服务器不会发送主体内容,而浏览器会使用其缓存。但由于没有缓存,所以您会得到一个空白页面。 - h345k34cr
1
@AkshatJiwanSharma 任何程序都是为了完全满足产品所有者的合同而开发的。产品所有者拥有代码并支付费用,而不是一些写纸张无人关注的组织。如果合同规定“200”,那么任何不等于“200”的状态都是一个错误。当出现错误时,我必须重写代码,直到一切都符合预期。W3C在这件事上没有发言权。 - Gherman
显示剩余2条评论
7个回答

140

7
你能解释一下“最简单的解决方案”是什么,或者提供一个相关影响的参考吗? - Samuel Méndez
2
@SamuelMéndez 它基本上禁用了缓存,关于ETag的维基百科有很多好的信息 https://en.wikipedia.org/wiki/HTTP_ETag - blented
对我有用 :) - Naveen Kumar V
1
这很有道理。我相信Etag没有被正确地重新计算,因为我没有设置最后修改日期,如vlasenko的链接中所提到的。当我按照以下方式更新代码时,我的问题消失了:const headers = { 'Last-Modified': (new Date()).toUTCString() }; app.get('/*', (req, res) => { res.sendFile(join(DIST_FOLDER + '/index.html'), { headers } ); }); - Robert Patterson

5

尝试在Safari中使用私密浏览或删除整个缓存/cookie。

我在使用Chrome时遇到了类似的问题,当浏览器认为它已经将网站保存在缓存中,但实际上并没有。

使服务器响应304的http请求部分是etag。看起来Safari发送正确的etag,但没有相应的缓存。


当我尝试删除整个缓存时,那对我有效,谢谢。 - Yuttanant Suwansiri

3

我在Safari和Chrome (我测试的两个浏览器) 中遇到了同样的问题,但我刚刚做了一些似乎有效的事情,至少自从我加入这个解决方案后,我就无法再重现这个问题了。我所做的是在标题中添加一个生成时间戳的元标签。虽然看起来有点奇怪,但它很简单 :)

<meta name="304workaround" content="2013-10-24 21:17:23">

更新 附言: 据我所知,当我移除我的节点代理(我指的是express.vhost和http-proxy模块)时,问题消失了,这很奇怪...


我也使用了Apache代理,这可能是问题所在。我的解决方法是仅针对具有HTTP头的内容站点禁用缓存。 - h345k34cr
通过头部禁用缓存绝对是正确的方法。起初它对我无效,但现在有效了。换句话说,第一次肯定是我哪里犯了错误 :) - user907567

3
如您所说,Safari 在重新加载时会发送 Cache-Control: max-age=0。Express(或者更具体地说,是 Express 的依赖项 node-fresh)在收到 Cache-Control: no-cache 头部信息时认为缓存已过期,但未对 Cache-Control: max-age=0 进行同样的处理。据我所知,它可能应该这样做。但我不是一个缓存专家。
解决方法是将 node-fresh/index.js 中(当前)第 37 行更改为:
if (cc && cc.indexOf('no-cache') !== -1) return false;  

为了

if (cc && (cc.indexOf('no-cache') !== -1 ||
  cc.indexOf('max-age=0') !== -1)) return false;

我在我的项目的package.json中通过npm复制了node-fresh和express,并做了修复。您也可以这样做。例如,以下是我的分支链接:https://github.com/stratusdata/node-freshhttps://github.com/stratusdata/express#safari-reload-fix。 safari-reload-fix分支基于3.4.7标签。

干得好!我看到express 3.5.1通过node-fresh 0.2.2包含了你的修复。 - Clafou
实际上,我错了,你的修复已经被还原,并没有真正进入0.2.2版本。仍然没有新的/快速修复。 - Clafou

2
老问题,我知道。禁用缓存设施是不必要的,也不是管理问题的最佳方式。通过禁用缓存设施,服务器需要更加努力地工作并生成更多的流量。此外,浏览器和设备需要更加努力,尤其是在移动设备上可能会出现问题。
使用Shift键+重新加载按钮可以轻松解决空白页面问题。
空白页面可能是以下原因之一:
- 代码中的错误。 - 在测试期间提供了一个空白页面(您可能无法记住),该页面被浏览器缓存。 - Safari中的错误(如果是这样,请向苹果报告它,不要尝试自行修复)。
首先尝试Shift键+重新加载按钮,看看问题是否仍然存在,并检查您的代码。

1
  • 操作系统:Windows
  • 浏览器:Chrome

我使用了 Ctrl + F5 键盘组合。这样做,我想获取一个新的响应而不是从缓存中读取。解决方法是对页面进行硬刷新。

MDN Web Docs 上:

"HTTP 304未修改客户端重定向响应代码表示无需重新传输请求的资源。它是到缓存资源的隐式重定向。"


-5
// just add * in URL
    
app.get('/api*', (req, res)=>{

// do something 

});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接