AMP

AMP Camp:AMP 中的跨源用户状态

网站

tl;dr:本文将指导您如何在您的域名和 AMP 缓存之间追踪用户操作。

欢迎阅读我们关于 AMP Camp 的系列文章的最新内容,我们的演示展示了如何使用 AMP 创建一个互动网站!在本系列文章中,我们将讨论我们用于创建该网站的技术和工具,以及我们制定的最佳实践。如果您有兴趣使用 AMP 创建一个互动网站,我们希望您能有所收获!

上一篇文章中,我们讨论了如何在客户端和您的服务器上使用模板。在本文中,我们将讨论在您的源站和 AMP 缓存之间追踪用户状态的最佳实践。

用户状态和 AMP 缓存

如果用户在 AMP 缓存上访问您的网站,然后又在您自己的域名上访问,则识别这两个访问者是同一个人非常重要。这可能需要一些工作。幸运的是,有一个解决方案!

想象一下:您经营着 BestClips,一个销售世界上最有效的回形针的网站。每个回形针最多可容纳 50 张纸,并且完全由 100% 可生物降解的大豆制成!

由于您希望用户尽快看到您的回形针,因此您使用 AMP 构建了您的产品页面。您从您的域名 bestclips.com 提供 AMP 页面。当 Google 和 Bing 等网络爬虫发现这些 AMP 页面时,它们会存储在 AMP 缓存中。因此,当用户在 Google 或 Bing 搜索中发现您的产品页面时,他们将在 google.com 或 bing.com 等网站上的 iframe 中查看该页面,并且该页面将从 AMP 缓存(例如 cdn.ampproject.org 或 bing-amp.com)提供。到目前为止,一切都很好!

但是,如果用户在 AMP 缓存上发现了您的页面,他们将回形针添加到他们的购物车,然后在当天晚些时候他们再次在 bestclips.com 上访问您的网站,会发生什么情况?那些回形针是否仍然在他们的购物车中?还是购物车将是空的?

(如果您的网站使用 已签名的交换许多浏览器 即使您的页面已从缓存中提供,也会显示您的原始域。在这种情况下,此问题将消失。否则,了解如何处理它很重要。)

问题是什么?

AMP 缓存有助于加快您的网页速度,同时 保护用户隐私!但缓存还引入了一个额外的复杂性级别:用户不仅可以在您的域上访问您的网站,还可以在缓存的域上访问。

假设 bestclips.com 遵循标准网络操作,通过放置包含会话 ID 的 Cookie 来跟踪用户的状态。然后,每当用户访问您在 bestclips.com 上的页面时,您的服务器都会检索 Cookie,读取会话 ID,并从与该 ID 关联的存储在您服务器上的数据中恢复用户状态。

现在,假设用户访问您的产品页面,他们看到了一些他们无法缺少的回形针,并且他们希望将这些回形针添加到他们的购物车。他们单击一个按钮,您的网站会将数据提交给您的服务器

<form action-xhr="/add-to-cart" method="POST">Code language: HTML, XML (xml)

如果用户在您的原始位置,则请求会附带一个会话 Cookie。部分请求将如下所示

POST /add-to-cart HTTP/2.0
Cookie: session_id=12345Code language: HTTP (http)

但是,如果用户在 AMP 缓存上访问您的网站,则对您服务器的请求实际上可能来自 ampproject.org 或 bing-amp.com - 一个不同的域!浏览器将您的 Cookie 与 bestclips.com 关联,因此它现在是一个第三方 Cookie。大多数浏览器都会愉快地发送这些 Cookie。但用户可能已将浏览器设置为阻止第三方 Cookie。在某些情况下,一些浏览器会简单地阻止第三方 Cookie。这将使您的请求看起来像这样,没有 Cookie 标头

POST /add-to-cart HTTP/2.0Code language: HTTP (http)

该怎么做?

简而言之,解决方案

(为简单起见,以后我们将使用术语“原始位置”来指您的域,而“缓存”来指 AMP 缓存。)

解决方案有两个方面。在您的域上,像往常一样使用会话 Cookie 识别用户。在缓存和接受第三方 Cookie 的浏览器上,执行相同的操作。否则,每当用户执行修改应用程序状态的操作时,立即将他们重定向到您的原始位置,您可以在其中访问或创建存储在您域下的 Cookie,然后进行所需的更改。

换句话说,如果用户希望将回形针添加到其购物车,并且您无法读取其 Cookie,请不要惊慌!只需将他们重定向到您的原始位置,您可以在那里根据自己的喜好更改他们的购物车。

此重定向由一个名为 AMP-Redirect-To 的 AMP 特定 HTTP 头实现。如果 AMP 页面使用 <amp-form> 发出服务器请求,并且服务器的响应包含此头,则 AMP 将重定向到所需页面。

以下是整个流程

  1. 用户导航到产品页面。如果用户在原点,则原点会设置会话 Cookie(如果尚未存在)。
  2. 用户执行操作以更改购物车中的内容
  3. 浏览器通过 POST XHR 将有关更改的数据发送到原点
  4. 原点检查请求是否不包含会话 Cookie 且是否来自缓存
    • 如果为真
      1. 响应告诉 AMP 重定向到原点上的一个 URL,其中包含描述用户更改的查询字符串
      2. 当原点看到该查询字符串时,它会读取或创建 Cookie,进行更改,然后再次重定向到原点上的一个 URL,该 URL 不包含该讨厌的查询字符串
    • 如果为假,那么我们可以简单地检索用户的会话并进行用户的更改。我们要么在原点上,要么在允许第三方 Cookie 的浏览器中使用缓存。

无论用户最初在缓存还是原点上,在这个过程结束时,他们都拥有了一个会话,并且他们的更改已反映在服务器上。

解决方案,详细说明

让我们详细描述此过程。假设用户访问我们的产品页面并决定购买我们的一款新 Superclip。我们的产品页面位于 https://bestclips.com/product,但用户可以在我们的原点或 AMP 缓存中访问此页面。

步骤 1. 用户到达我们的产品页面。此页面包含一个允许用户选择数量的表单,以及一个允许他们将产品添加到购物车的提交按钮。这可能如下所示

<form action-xhr="/api/add-to-cart" method="POST">
	<select name="quantity">
		<option value="0">0</option>
		<option value="1" selected>1</option>
		<option value="2">2</option>
	</select>
	<input type="submit" value="Add to Cart">
</form>Code language: HTML, XML (xml)

步骤 2. 假设用户将数量保留为“1”,然后点击“添加到购物车”。

步骤 3. 表单已提交,AMP 向我们的服务器 bestclips.com 发送了一个 XHR POST 请求。(为确保此请求即使从缓存中也能正常工作,您需要 设置 CORS 头。如果您使用的是 node,则只需插入 AMP CORS 中间件。)在原点上,请求如下所示

POST /api/add-to-cart HTTP/2.0
AMP-Same-Origin: true
Cookie: session_id=12345
quantity=2Code language: JavaScript (javascript)

请注意,当 AMP 在原点上运行时,它会方便地添加 AMP-Same-Origin 头。

如果用户在允许第三方 Cookie 的浏览器中使用缓存,则请求如下所示

POST /api/add-to-cart HTTP/2.0
Cookie: session_id=12345
quantity=2

如果用户在阻止第三方 Cookie 的浏览器中使用缓存,则 Cookie 头也将缺失

POST /api/add-to-cart HTTP/2.0
quantity=2

步骤 4. 现在进入有趣的部分。

服务器检查请求是否缺少会话 cookie 且来自缓存。如果没有 cookie,则浏览器不允许设置 cookie。如果用户的浏览器阻止了所有 cookie,也会发生这种情况,在这种情况下,重定向到原点不会有帮助。我们永远无法使用 cookie 跟踪用户的状态。这就是我们还要检查请求是否来自缓存的原因——因为这是我们可以处理的情况。

如果请求缺少会话 cookie 且来自缓存,则请求将同时缺少 Cookie 标头和 AMP-Same-Origin 标头,如上所示。服务器检测到此条件

if (!request.cookies.session_id && request.headers['amp-same-origin'] !== 'true')Code language: JavaScript (javascript)

如果为真,则服务器发送一个响应,指示 AMP 重定向到原点上的 URL。在该 URL 中,它包含一个描述用户更改的查询字符串,如下所示

response.setHeader("AMP-Redirect-To", `https://bestclips.com?item=${request.body.itemName}&quantity=${request.body.quantity}`);Code language: JavaScript (javascript)

这会发送一个包含如下标头的响应

AMP-Redirect-To:https://bestclips.com/product?item=superclip&quantity=1Code language: JavaScript (javascript)

当此响应返回到浏览器时,AMP 会注意到标头并重定向到https://bestclips.com/product?item=superclip&quantity=1。此请求转到 bestclips.com,我们的原点!然后,原点服务器可以读取会话 cookie 或在不存在时创建一个会话 cookie。它将一个 superclip 添加到用户的购物车中。然后它重定向到https://bestclips.com/product

换句话说,它重定向回产品页面而不带查询字符串。这样,用户就不会遇到查询字符串可能导致的困难。(请参见下面的“以下是如何不这样做的”了解原因。)

正如承诺的那样,现在用户拥有了一个会话 cookie,并且更改已在服务器上进行。

我可以使用客户端 ID 吗?

如果您使用过 AMP,您可能知道有客户端 ID,它允许分析包跟踪用户从缓存到原点的旅程。在原点上,它存储在 cookie 中并持续一年。在缓存中,它也存储在 cookie 中,如果它不在 cookie 中,则可以通过调用客户端 ID API 来创建它。因此,使用它来始终如一地识别用户会很诱人。

不幸的是,尽管网站确实使用了此解决方案,但它存在缺陷。客户端 ID 唯一地识别单个用户在原点和缓存之间进行的某些旅程,但并非全部。而且,它的跨站点行为可能会被我们在本文中一直在处理的同一浏览器阻止。

由于 AMP Linker 将客户端 ID 保留为查询字符串参数,因此这些跨网站旅程更加可靠。这提供了另一种使用客户端 ID 的方法!但这意味着用户的唯一标识符将显示在他们的 URL 中。URL 往往会记录在服务器上,有时不法分子会发现这些日志文件。更糟糕的是,用户可能会公开分享他们的 URL,向全世界暴露他们的标识符。无论哪种情况,他们的会话都容易受到黑客攻击!这就是我们在上面的示例中使用 POST 而不是 GET 的原因。

我真有必要这样做吗?

可能不需要。但随着浏览器在越来越多的情况下阻止第三方 cookie,此解决方案对于让用户在您的原点和 AMP 缓存中顺畅地使用您的网站将变得越来越重要。虽然上述流程需要一些时间来解释,但其实并不难实现。

真的吗?

了解我们在 AMP Camp 演示网站 中如何做到这一点。这是 服务器代码。这是 产品页面上的表单。唯一的区别是,在此演示网站上,当用户向购物车添加商品时,我们会将他们重定向到购物车详情页面。

作者:Ben Morss,开发人员倡导者