使用 Service Worker 缓存非 SSL 请求

Cache non-SSL request with Service Worker

因为 Service Worker registration requires website to be served over HTTPS, Google Chrome throws an error when I try to add HTTP resource to Cache:

Mixed Content: The page at 'https://example.com/sw.js' was loaded over HTTPS, but requested an insecure resource 'http://example.com/'. This request has been blocked; the content must be served over HTTPS.

我想缓存主 HTTP 页面(或至少 301 重定向到 HTTPS),因为用户很难输入 https:// 部分,尤其是在移动设备上,以访问我网站的缓存版本,离线时。

是否有任何解决方法可以实现此目的?

我已经考虑了一下,但我认为没有直接解决您遇到的问题的方法。跨源策略将阻止您在本地缓存它,这是有充分理由的。如果这没有到位,您可以拦截对其他来源的任何请求。

https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API 说 “出于安全原因,服务工作者仅 运行 通过 HTTPS。修改网络请求对中间人攻击开放将非常糟糕。

作为一种解决方案,我将在 http 上托管一个应用程序缓存清单,并重定向到 https。如果用户第一次通过 https 访问,这不会自动缓存,但如果是这种情况,用户很可能总是通过 https 访问该页面。如果用户确实使用 http,它将缓存重定向,并且这将可离线使用。虽然该技术已被弃用,但仍被广泛使用,参考:https://developer.mozilla.org/en-US/docs/Web/HTML/Using_the_application_cache .

如果还使用 301 永久重定向,则浏览器不太可能缓存重定向并默认加载 https,请参阅:https://en.wikipedia.org/wiki/HTTP_301

希望这足以满足您的需求,如果不能,我想讨论一下您在使用此解决方案时遇到的问题。

我不认为这个问题可以通过 service worker 来解决,因为没有办法拦截对 http 方案的请求。但是,看起来 Strict-Transport-Security header 可以工作。

如果在 https 响应中提供,它会强制浏览器始终使用 https 方案来加载页面——如果已正确安装,这应该会影响服务工作者。这适用于用户未键入方案(例如 example.com)或键入 http 方案(例如 http://example.com)的情况。

如果 server-side 解决方案,例如返回 HTTP 301 响应或包含 Strict-Transport-Security header 由于您的服务设置而无法实现,则另一种选择是使用 client-side JavaScript 更新 window.location.href,这将触发页面重新加载。

您可以遵循 <platinum-https-redirect> Polymer element 中使用的技术,改编如下:

function _isLocalhost(hostname) {
  // !! coerces the logical expression to evaluate to the values true or false.
  return !!(hostname === 'localhost' ||
            // [::1] is the IPv6 localhost address.
            hostname === '[::1]' ||
            // 127.0.0.1/8 is considered localhost for IPv4.
            hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/));
}

if (window.location.protocol === 'http:' && !_isLocalhost(window.location.hostname)) {
  // Redirect to https: if we're currently using http: and we're not on localhost.
  window.location.href = 'https:' + window.location.href.substring(window.location.protocol.length);
}