

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 使用 Lambda@Edge 在邊緣自訂
<a name="lambda-at-the-edge"></a>

Lambda@Edge 是 的延伸 AWS Lambda。Lambda@Edge 是運算服務，可讓您執行函數，以自訂 Amazon CloudFront 交付的內容。您可以在 Lambda 主控台中的 AWS 區域美國東部 （維吉尼亞北部） 撰寫 Node.js 或 Python 函數。

建立函數後，您可以使用 Lambda 主控台或 CloudFront 主控台新增觸發，讓函數在更接近檢視器 AWS 的位置執行，而無需佈建或管理伺服器。或者您可以使用 Lambda 和 CloudFront API 操作，以程式設計方式設定函數和觸發。

Lambda@Edge 會自動擴展，從每天幾個請求自動擴展到每秒數千個請求。在更接近瀏覽者而非原始伺服器 AWS 的位置處理請求，可大幅降低延遲並改善使用者體驗。

**注意**  
gRPC 請求不支援 Lambda@Edge。如需更多詳細資訊，請參閱 [使用 gRPC 搭配 CloudFront 分佈](distribution-using-grpc.md)。

**Topics**
+ [

# Lambda@Edge 如何處理請求和回應
](lambda-edge-event-request-response.md)
+ [

# 使用 Lambda@Edge 的方式
](lambda-edge-ways-to-use.md)
+ [

# 開始使用 Lambda@Edge 函數 (主控台)
](lambda-edge-how-it-works.md)
+ [

# 設定 Lambda@Edge 的 IAM 許可權限和角色
](lambda-edge-permissions.md)
+ [

# 撰寫及建立 Lambda@Edge 函數
](lambda-edge-create-function.md)
+ [

# 為 Lambda@Edge 函數新增觸發條件
](lambda-edge-add-triggers.md)
+ [

# 測試和偵錯 Lambda@Edge 函數
](lambda-edge-testing-debugging.md)
+ [

# 刪除 Lambda@Edge 函數和複本
](lambda-edge-delete-replicas.md)
+ [

# Lambda@Edge 事件結構說明頁面
](lambda-event-structure.md)
+ [

# 使用請求和回應
](lambda-generating-http-responses.md)
+ [

# Lambda@Edge 範例函數
](lambda-examples.md)

# Lambda@Edge 如何處理請求和回應
<a name="lambda-edge-event-request-response"></a>

當您將 CloudFront 分佈與 Lambda@Edge 函數建立關聯時，CloudFront 會攔截在 CloudFront 節點的請求和回應。您可以在發生下列 CloudFront 事件時執行 Lambda 函數：
+ 當 CloudFront 接收到來自檢視器的請求 (檢視器請求) 時
+ 在 CloudFront 轉寄請求至來源 (原始請求) 之前
+ 當 CloudFront 接收到來自來源的回應 (原始回應) 時
+ 在 CloudFront 傳回回應給檢視器 (檢視器回應) 之前

如果您使用的是 AWS WAF，則會在套用任何 AWS WAF 規則後執行 Lambda@Edge 檢視器請求。

如需詳細資訊，請參閱[使用請求和回應](lambda-generating-http-responses.md)及[Lambda@Edge 事件結構說明頁面](lambda-event-structure.md)。

# 使用 Lambda@Edge 的方式
<a name="lambda-edge-ways-to-use"></a>

您的 Amazon CloudFront 分佈有許多 Lambda@Edge 處理用途，例如下列範例：
+ Lambda 函數可檢查 Cookie 和重新寫入 URL，讓使用者看到不同版本網頁的 A/B 測試。
+ CloudFront 可以檢視 `User-Agent` 標頭來根據檢視器使用的裝置傳回不同的物件給檢視器，其中包含裝置相關資訊。例如，根據檢視器裝置的螢幕尺寸，CloudFront 可以傳回不同的圖片大小。同樣地，該函數可以考慮 `Referer` 標頭中的值，讓 CloudFront 傳回最低可用解析度圖片給機器人。
+ 或者，您可以檢查 Cookie 的其他條件。例如，在銷售衣服的零售網站，如果您使用 Cookie 以顯示使用者選擇什麼顏色的夾克，該 Lambda 函數可以變更請求，讓 CloudFront 傳回所選顏色的夾克圖片。
+ 發生 CloudFront 檢視器請求或原始伺服器請求事件時，Lambda 函數可產生 HTTP 回應。
+ 函數可以檢查標頭或授權字符，在 CloudFront 轉送請求到您的原始伺服器之前，插入標頭來控制存取您的內容。
+ Lambda 函數也可以讓網路呼叫外部資源，確認使用者登入資料，或擷取額外的內容以自訂回應。

如需包含範例程式碼的詳細資訊，請參閱 [Lambda@Edge 範例函數](lambda-examples.md)。

如需有關在主控台設定 Lambda@Edge 的詳細資訊，請參閱 [教學課程：建立基本 Lambda@Edge 函數 (主控台)](lambda-edge-how-it-works-tutorial.md)。

# 開始使用 Lambda@Edge 函數 (主控台)
<a name="lambda-edge-how-it-works"></a>

Lambda@Edge 可讓您使用 CloudFront 觸發條件來調用 Lambda 函數。當您將 CloudFront 分佈與 Lambda 函數建立關聯時，CloudFront 會在 CloudFront 節點[攔截請求和回應](https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html)，並執行該函數。Lambda 函數可以提高安全性，或是自訂與檢視器密切相關的資訊來提升效能。

下列清單提供基本概觀，協助瞭解如何透過 CloudFront 建立和使用 Lambda 函數。

**概觀：透過 CloudFront 建立和使用 Lambda 函數**

1. 在美國東部 (維吉尼亞北部) 區域建立 Lambda 函數。

1. 儲存並發佈有編號的函數版本。

   如果您想要變更函數，您必須編輯美國東部 (維吉尼亞北部) 區域內 \$1LATEST 版本的函數。然後，在您設定它來搭配 CloudFront 使用之前，請發佈新編號的版本。

1. 將函數與 CloudFront 分佈和快取行為建立關聯。然後，指定可以使函數執行的一或多個 CloudFront 事件 (*觸發條件*)。例如，您可以建立當 CloudFront 接收來自檢視器的請求時，促使函數執行的觸發條件。

1. 您建立觸發條件時，Lambda 會在全球各地的 AWS 位置建立函數複本。

**提示**  
如需更多詳細資訊，請參閱[如何建立和更新函數](lambda-edge-create-function.md)、[事件結構](lambda-event-structure.md)，以及[如何新增 CloudFront 觸發條件](lambda-edge-add-triggers.md)。您也可以在[Lambda@Edge 範例函數](lambda-examples.md)中找到更多想法並取得程式碼範例。

如需逐步教學課程，請參閱下列主題：

**Topics**
+ [

# 教學課程：建立基本 Lambda@Edge 函數 (主控台)
](lambda-edge-how-it-works-tutorial.md)

# 教學課程：建立基本 Lambda@Edge 函數 (主控台)
<a name="lambda-edge-how-it-works-tutorial"></a>

本教學課程將為您示範如何開始使用 Lambda@Edge，協助您建立及設定在 CloudFront 中執行的 Node.js 函數範例。此項範例會在 CloudFront 檢索檔案時，將 HTTP 安全性標頭新增到回應。(這可以改善網站的安全性和隱私。)

本教學課程不需要您自己的網站。不過您建立自己的 Lambda@Edge 解決方案時，會依照類似的步驟進行，並從相同的選項中選擇。

**Topics**
+ [

## 步驟 1：註冊 AWS 帳戶
](#lambda-edge-how-it-works-tutorial-AWS)
+ [

## 步驟 2：建立 CloudFront 分佈
](#lambda-edge-how-it-works-tutorial-cloudfront)
+ [

## 步驟 3：建立函數
](#lambda-edge-how-it-works-tutorial-create-function)
+ [

## 步驟 4：新增一個 CloudFront 觸發條件來執行該函數
](#lambda-edge-how-it-works-tutorial-add-trigger)
+ [

## 步驟 5：驗證函數正常執行
](#lambda-edge-how-it-works-tutorial-verify)
+ [

## 步驟 6：排除問題
](#lambda-edge-how-it-works-tutorial-troubleshoot)
+ [

## 步驟 7：清除範例資源
](#lambda-edge-how-it-works-tutorial-cleanup-resources)
+ [

## 相關資訊
](#lambda-edge-how-it-works-tutorial-resources)

## 步驟 1：註冊 AWS 帳戶
<a name="lambda-edge-how-it-works-tutorial-AWS"></a>

若您尚未註冊，請先註冊 AWS 帳戶。如需詳細資訊，請參閱[註冊 AWS 帳戶](setting-up-cloudfront.md#sign-up-for-aws)。

## 步驟 2：建立 CloudFront 分佈
<a name="lambda-edge-how-it-works-tutorial-cloudfront"></a>

在建立 Lambda@Edge 範例函數之前，您必須有一個可使用、包含提供內容之來源的 CloudFront 環境。

在此範例中，您會建立 CloudFront 分佈，此分佈是使用 Amazon S3 儲存貯體做為分佈的原始伺服器。如果您已有環境可使用，可以略過此步驟。<a name="lambda-edge-how-it-works-tutorial-cf-proc"></a>

**使用 Amazon S3 原始伺服器建立 CloudFront 分佈**

1. 使用一兩個檔案來建立 Amazon S3 儲存貯體，例如範例內容適用的映像檔案。如需說明，請遵循[將您的內容上傳到 Amazon S3](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.html#GettingStartedUploadContent) 中的步驟。請確定您有設定對應的許可，以授予儲存貯體中物件的公有讀取權限。

1. 請依照[建立 CloudFront Web 分佈](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.html#GettingStartedCreateDistribution)中的步驟來建立 CloudFront 分佈，並新增您的 S3 儲存貯體當做原始伺服器。如果您已有分佈，可以改為新增儲存貯體當做該分佈的來源。
**提示**  
請記下您的分佈 ID。稍後您在本教學課程中為函數新增 CloudFront 觸發條件時，您必須在下拉式清單中選擇分佈的 ID，例如 `E653W22221KDDL`。

## 步驟 3：建立函數
<a name="lambda-edge-how-it-works-tutorial-create-function"></a>

在這個步驟中，您將從 Lambda 主控台提供的藍圖範本開始建立 Lambda 函數。此函數會新增程式碼來更新您的 CloudFront 分佈中的安全標頭。<a name="lambda-edge-how-it-works-tutorial-create-function-blueprint-proc"></a>

**建立 Lambda 函數**

1. 登入 AWS 管理主控台 並開啟位於 的 AWS Lambda 主控台[https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/)。
**重要**  
請確定您位於美國**US-East-1（維吉尼亞北部） ** AWS 區域 (**us-east-1**)。您必須位在此區域，才能建立 Lambda@Edge 函數。

1. 選擇 **Create function (建立函數)**。

1. 在**建立函數**頁面上，選擇**使用藍圖**，然後在搜尋欄位中輸入 **cloudfront** 以篩選 CloudFront 藍圖。
**注意**  
CloudFront 藍圖僅適用於**美國東部 1 (維吉尼亞北部)** 區域 (**us-east-1**)。

1. 選擇**修改 HTTP 回應標頭**藍圖做為函數的範本。

1. 輸入以下有關函數的資訊：
   + **函數名稱**：輸入函數的名稱。
   + **執行角色**：選擇如何為函數設定許可權限。若要使用建議的基本 Lambda@Edge 許可政策範本，請選擇**從 AWS 政策範本建立新角色**。
   + **角色名稱**：輸入政策範本建立的角色名稱。
   + **政策範本**：Lambda 會自動新增**基本 Lambda@Edge 許可權限**政策範本，因為您選擇 CloudFront 藍圖做為您的函數基礎。此原則範本會新增執行角色許可，讓 CloudFront 能夠在全球的 CloudFront 位置為您執行 Lambda 函數。如需詳細資訊，請參閱[設定 Lambda@Edge 的 IAM 許可權限和角色](lambda-edge-permissions.md)。

1. 請在頁面底部選擇**建立函數**。

1. 在顯示的**部署至 Lambda@Edge** 窗格中選擇**取消**。(在本教學課程中，您必須先修改函數程式碼，才能將函數部署至 Lambda@Edge。)

1. 向下捲動至頁面的**程式碼來源**區段。

1. 使用修改原始伺服器傳回之安全性標頭的函數，來取代範本程式碼。例如，您可能會使用與下列類似的程式碼：

   ```
   'use strict';
   export const handler = (event, context, callback) => {
   
       //Get contents of response
       const response = event.Records[0].cf.response;
       const headers = response.headers;
   
       //Set new headers
       headers['strict-transport-security'] = [{key: 'Strict-Transport-Security', value: 'max-age= 63072000; includeSubdomains; preload'}];
       headers['content-security-policy'] = [{key: 'Content-Security-Policy', value: "default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'"}];
       headers['x-content-type-options'] = [{key: 'X-Content-Type-Options', value: 'nosniff'}];
       headers['x-frame-options'] = [{key: 'X-Frame-Options', value: 'DENY'}];
       headers['x-xss-protection'] = [{key: 'X-XSS-Protection', value: '1; mode=block'}];
       headers['referrer-policy'] = [{key: 'Referrer-Policy', value: 'same-origin'}];
   
       //Return modified response
       callback(null, response);
   };
   ```

1. 選擇**檔案**，然後選擇**儲存**以儲存更新後的程式碼。

1. 選擇**部署**。

繼續依照下一節的步驟，新增 CloudFront 觸發條件以執行函數。

## 步驟 4：新增一個 CloudFront 觸發條件來執行該函數
<a name="lambda-edge-how-it-works-tutorial-add-trigger"></a>

既然您已擁有用來更新安全標頭的 Lambda 函數，您可以設定 CloudFront 觸發條件來執行函數，在 CloudFront 從您的分佈原始伺服器收到的任何回應中新增標頭。<a name="lambda-edge-how-it-works-tutorial-add-trigger-proc"></a>

**設定函數的 CloudFront 觸發條件**

1. 在 Lambda 主控台頁面的**函數概覽**頁面中，選擇**新增觸發條件**。

1. 針對**觸發組態**選擇 **CloudFront**。

1. 選擇**部署到 Lambda@Edge**。

1. 在**部署到 Lambda@Edge** 面板中，在**設定 CloudFront 觸發條件**下輸入以下資訊：
   + **分佈**：要與函數建立關聯的 CloudFront 分佈 ID。在下拉式清單中選擇分佈 ID。
   + **快取行為**：要用於觸發條件的快取行為。在此範例中，請將值的設定保留為 **\$1**，這代表分佈的預設快取行為。如需詳細資訊，請參閱 [所有分佈設定參考](distribution-web-values-specify.md) 主題中的 [快取行為設定](DownloadDistValuesCacheBehavior.md)。
   + **CloudFront 事件**：指定函數何時執行的觸發條件。我們希望每當 CloudFront 從原始伺服器傳回回應時，都會執行安全標頭函數。因此請在下拉式清單中選擇**原始伺服器回應**。如需詳細資訊，請參閱[為 Lambda@Edge 函數新增觸發條件](lambda-edge-add-triggers.md)。

1. 選取**確認部署到 Lambda@Edge** 核取方塊。

1. 選擇**部署**以新增觸發條件，並將函數複寫至全球 AWS 位置。

1. 請等候函數完成複寫。這通常需要幾分鐘的時間。

    若要檢查複寫是否已完成，請[移至 CloudFront主控台](https://console.aws.amazon.com/cloudfront/v4/home)並檢視您的分佈。等待分佈的狀態是否已從**正在部署**變為日期和時間 (表示您的函數已複寫完成)。若要驗證函數是否可正常運作，請依照下一節的步驟進行。

## 步驟 5：驗證函數正常執行
<a name="lambda-edge-how-it-works-tutorial-verify"></a>

既然您已建立 Lambda 函數並設定觸發條件來為 CloudFront 分佈執行該函數，接下來請進行檢查，以確定函數會如預期執行。在此範例中，我們會檢查 CloudFront 傳回的 HTTP 標頭，以確認安全標頭已新增。<a name="lambda-edge-how-it-works-tutorial-verify-proc"></a>

**驗證您的 Lambda@Edge 函數有新增安全標頭**

1. 在瀏覽器中，輸入 S3 儲存貯體中某個檔案的 URL。例如，您可以使用類似以下的 URL：`https://d111111abcdef8.cloudfront.net/image.jpg`。

   如需用於檔案 URL 中 CloudFront 網域名稱的詳細資訊，請參閱[自訂 CloudFront 中檔案的 URL 格式](LinkFormat.md)。

1. 開啟瀏覽器的網頁開發人員工具列。例如，在 Chrome 瀏覽器視窗中，開啟內容 (按一下滑鼠右鍵) 選單，然後選擇 **Inspect (檢查)**。

1. 選擇 **Network (網路)** 索引標籤。

1. 重新載入頁面來查看您的影像，然後選擇左窗格中的 HTTP 請求。您會看到 HTTP 標頭顯示在個別的窗格中。

1. 查看 HTTP 標頭清單，以驗證預期的安全標頭包含在清單中。例如，您可能會看到類似於以下螢幕擷取畫面所示的標頭。  
![\[HTTP 標頭清單，所要的安全標頭已採用突出顯示。\]](http://docs.aws.amazon.com/zh_tw/AmazonCloudFront/latest/DeveloperGuide/images/lambda-at-edge-security-headers-list.png)

如果安全標頭包含在您的標頭清單中，那就太棒了！這表示您已成功建立第一個 Lambda@Edge 函數。如果 CloudFront 傳回錯誤或有其他問題，請繼續下一個步驟來排除問題。

## 步驟 6：排除問題
<a name="lambda-edge-how-it-works-tutorial-troubleshoot"></a>

如果 CloudFront 傳回錯誤或是未依照預期來新增安全標頭，您可以查看 CloudWatch Logs 來調查函數的執行狀況。請務必使用存放在最 AWS 接近執行函數之位置的日誌。

例如，如果您從倫敦檢視檔案，請試著在 CloudWatch 主控台將區域變更為歐洲 (倫敦)。<a name="lambda-edge-how-it-works-tutorial-cloudwatch-proc"></a>

**檢查您 Lambda@Edge 函數的 CloudWatch Logs**

1. 登入 AWS 管理主控台 ，並在 https：//[https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/) 開啟 CloudWatch 主控台。

1. 將 **Region (區域)** 變更為您在瀏覽器中檢視檔案時所顯示的位置。這是函數執行所在的位置。

1. 在左窗格中，選擇 **Logs (日誌)** 來檢視分佈的日誌。

如需詳細資訊，請參閱[使用 Amazon CloudWatch 監控 CloudFront 指標](monitoring-using-cloudwatch.md)。

## 步驟 7：清除範例資源
<a name="lambda-edge-how-it-works-tutorial-cleanup-resources"></a>

如果您僅為本教學課程建立 Amazon S3 儲存貯體和 CloudFront 分佈，請刪除您配置 AWS 的資源，以免再產生費用。刪除 AWS 資源後，您新增的任何內容將無法再使用。

**工作**
+ [刪除 S3 儲存貯體](#lambda-edge-how-it-works-tutorial-delete-bucket) 
+ [刪除 Lambda 函式](#lambda-edge-how-it-works-tutorial-delete-function)
+ [刪除 CloudFront 分佈](#lambda-edge-how-it-works-tutorial-delete-distribution)

### 刪除 S3 儲存貯體
<a name="lambda-edge-how-it-works-tutorial-delete-bucket"></a>

刪除 Amazon S3 儲存貯體之前，請務必停用儲存貯體的記錄。否則，在刪除日誌時， AWS 請繼續將日誌寫入您的儲存貯體。<a name="lambda-edge-how-it-works-tutorial-delete-bucket-proc"></a>

**停用儲存貯體的記錄**

1. 在以下網址開啟 Amazon S3 主控台：[https://console.aws.amazon.com/s3/](https://console.aws.amazon.com/s3/)。

1. 選取您的儲存貯體，然後選擇 **Properties (屬性)**。

1. 從 **Properties (屬性)** 選擇 **Logging (記錄日誌)**。

1. 清除 **Enabled (已啟用)** 核取方塊。

1. 選擇 **Save** (儲存)。

現在即可刪除儲存貯體。如需詳細資訊，請參閱 *Amazon Simple Storage Service 主控台使用者指南*中的[我該如何刪除 S3 儲存貯體？](https://docs.aws.amazon.com/AmazonS3/latest/userguide/delete-bucket.html)。

### 刪除 Lambda 函式
<a name="lambda-edge-how-it-works-tutorial-delete-function"></a>

如需刪除 Lambda 函數關聯和函數本身 (選用) 的說明，請參閱 [刪除 Lambda@Edge 函數和複本](lambda-edge-delete-replicas.md)。

### 刪除 CloudFront 分佈
<a name="lambda-edge-how-it-works-tutorial-delete-distribution"></a>

刪除 CloudFront 分佈前必須先將其停用。已停用的分佈如此即不再有用，也不會產生費用。您隨時都可以啟用之前停用的分佈。刪除停用的分佈之後，它即不再可供使用。<a name="lambda-edge-how-it-works-tutorial-delete-distribution-proc"></a>

**停用和刪除 CloudFront 分佈**

1. 在 [https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home) 開啟 CloudFront 主控台。

1. 選取您要停用的分佈，然後選擇 **Disable (停用)**。

1. 出現確認提示時，請選擇 **Yes, Disable (是，停用)**。

1. 選取已停用的分佈，然後選擇 **Delete (刪除)**。

1. 出現確認提示時，選擇 **Yes, Delete (是，刪除)**。

## 相關資訊
<a name="lambda-edge-how-it-works-tutorial-resources"></a>

現在您對於 Lambda@Edge 函數的運作方式已有了基本了解，請閱讀以下內容來進一步了解：
+ [Lambda@Edge 範例函數](lambda-examples.md)
+ [Lambda@Edge 設計最佳實務](https://aws.amazon.com/blogs/networking-and-content-delivery/lambdaedge-design-best-practices/)
+ [使用 Lambda@Edge 減少延遲並轉移運算至邊緣](https://aws.amazon.com/blogs/networking-and-content-delivery/reducing-latency-and-shifting-compute-to-the-edge-with-lambdaedge/)

# 設定 Lambda@Edge 的 IAM 許可權限和角色
<a name="lambda-edge-permissions"></a>

若要設定 Lambda@Edge，您必須具有以下適用於 AWS Lambda的 IAM 許可權限和角色：
+ [IAM 許可權限](#lambda-edge-permissions-required)：這些許可權限可讓您建立 Lambda 函數，並將其與 CloudFront 分佈建立關聯。
+ [Lambda 函數執行角色](#lambda-edge-permissions-function-execution) (IAM 角色) – Lambda 服務主體會擔任此角色來執行您的函數。
+ [Lambda@Edge 的服務連結角色](#using-service-linked-roles-lambda-edge) – 服務連結角色允許特定 AWS 服務 將 Lambda 函數複寫到 ， AWS 區域 並讓 CloudWatch 使用 CloudFront 日誌檔案。

## 將 Lambda@Edge 函數與 CloudFront 分佈產生關聯所需的 IAM 許可
<a name="lambda-edge-permissions-required"></a>

除了 Lambda 需要的 IAM 許可權限之外，IAM 使用者還需要下列 IAM 許可權限，以便將 Lambda 函數與 CloudFront 分佈建立關聯：
+ `lambda:GetFunction`：授予許可權限以取得 Lambda 函數的組態資訊，以及預先簽署的 URL，以下載包含該函數的 `.zip` 檔案。
+ `lambda:EnableReplication*`：授予許可權限至資源政策，讓 Lambda 複寫服務能夠取得函數程式碼和組態。
+ `lambda:DisableReplication*`：授予許可權限至資源政策，以便讓 Lambda 複寫服務可以刪除函數。
**重要**  
您必須在 `lambda:EnableReplication*` 和 `lambda:DisableReplication*` 動作的結尾新增星號 (`*`)。
+ 對於資源，指定要在 CloudFront 事件發生時執行之函數版本 ARN，如以下範例所示：

  `arn:aws:lambda:us-east-1:123456789012:function:TestFunction:2`
+ `iam:CreateServiceLinkedRole`：授予許可權限建立服務連結角色，供 Lambda@Edge 用來複寫 CloudFront 中的 Lambda 函數。第一次設定 Lambda@Edge 之後，會自動為您建立服務連結角色。您不需要將此許可權限新增至使用 Lambda@Edge 的其他分佈。

  
+ `cloudfront:UpdateDistribution` 或 `cloudfront:CreateDistribution`：授予許可權限以更新或建立分佈。

如需詳細資訊，請參閱下列主題：
+ [適用於 Amazon CloudFront 的 Identity and Access Management](security-iam.md)
+ 《*AWS Lambda 開發人員指南*》中的 [Lambda 資源存取許可權限](https://docs.aws.amazon.com/lambda/latest/dg/intro-permission-model.html#lambda-intro-execution-role)

## 服務主體的函數執行角色
<a name="lambda-edge-permissions-function-execution"></a>

您必須建立 `lambda.amazonaws.com` 和 `edgelambda.amazonaws.com` 服務主體可在執行函數時擔任的 IAM 角色。

**提示**  
在 Lambda 主控台中建立函數時，您可以選擇使用 AWS 政策範本建立新的執行角色。此步驟會*自動*新增執行函數所需的 Lambda@Edge 許可權限。請參閱[教學課程：建立簡單 Lambda@Edge 函數中的步驟 5](lambda-edge-how-it-works-tutorial.md#lambda-edge-how-it-works-tutorial-create-function)。

如需有關手動建立 IAM 角色的詳細資訊，請參閱《*IAM 使用者指南*》中的[建立角色及附加政策 (主控台)](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions_create-policies.html)。

**Example 範例：角色信任政策**  
您可在 IAM 主控台的**信任關係**索引標籤之下新增此角色。請勿在**許可權限**索引標籤下新增此政策。    
****  

```
{
   "Version":"2012-10-17",		 	 	 
   "Statement": [
      {
         "Effect": "Allow",
         "Principal": {
            "Service": [
               "lambda.amazonaws.com",
               "edgelambda.amazonaws.com"
            ]
         },
         "Action": "sts:AssumeRole"
      }
   ]
}
```

如需更多資訊瞭解授予執行角色所需的許可權限，請參閱《*AWS Lambda 開發人員指南*》中的 [Lambda 資源存取許可權限](https://docs.aws.amazon.com/lambda/latest/dg/intro-permission-model.html#lambda-intro-execution-role)。

**備註**  
根據預設，每當 CloudFront 事件觸發 Lambda 函數時，都會將資料寫入至 CloudWatch Logs。若要使用這些日誌，執行角色需要許可才能將資料寫入 CloudWatch Logs。您能使用預先定義的 AWSLambdaBasicExecutionRole 對執行角色授予許可權限。  
如需 CloudWatch Logs 的詳細資訊，請參閱[邊緣函數日誌](edge-functions-logs.md)。
如果您的 Lambda 函數程式碼存取其他 AWS 資源，例如從 S3 儲存貯體讀取物件，則執行角色需要執行該動作的許可。

## Lambda@Edge 的服務連結角色
<a name="using-service-linked-roles-lambda-edge"></a>

Lambda@Edge 使用 IAM [服務連結角色](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_terms-and-concepts.html#iam-term-service-linked-role)。服務連結角色是直接連結至服務的一種特殊 IAM 角色類型。服務連結角色由服務預先定義，並包含該服務在代表您呼叫其他 AWS 服務時，需要用到的所有權限。

Lambda@Edge 使用以下 IAM 服務連結角色：
+ **AWSServiceRoleForLambdaReplicator** – Lambda@Edge 使用此角色讓 Lambda@Edge 將函數複寫至 AWS 區域。

  您初次在 CloudFront 中新增 Lambda@Edge 觸發條件時，將會自動建立名為 AWSServiceRoleForLambdaReplicator 的角色，以便讓 Lambda@Edge 將函數複寫至 AWS 區域。使用 Lambda@Edge 函數時需要這個角色。AWSServiceRoleForLambdaReplicator 角色的 ARN 類似以下範例：

  `arn:aws:iam::123456789012:role/aws-service-role/replicator.lambda.amazonaws.com/AWSServiceRoleForLambdaReplicator`
+ **AWSServiceRoleForCloudFrontLogger**：CloudFront 使用此角色將日誌檔案推送至 CloudWatch。您可以使用日誌檔案來偵錯 Lambda@Edge 驗證錯誤。

  您新增 Lambda@Edge 函數關聯，讓 CloudFront 將 Lambda@Edge 錯誤日誌檔案推送至 CloudWatch 時，會自動建立名為 AWSServiceRoleForCloudFrontLogger 的角色。AWSServiceRoleForCloudFrontLogger 角色的 ARN 看起來類似如下：

  `arn:aws:iam::account_number:role/aws-service-role/logger.cloudfront.amazonaws.com/AWSServiceRoleForCloudFrontLogger`

服務連結角色可讓設定及使用 Lambda@Edge 變得更輕鬆，因為您不必手動新增必要的許可。Lambda@Edge 會定義其服務連結角色的許可，而且只有 Lambda@Edge 能夠擔任此角色。已定義的許可包括信任政策和許可政策。許可政策無法連接到其他任何 IAM 實體。

您必須移除任何關聯的 CloudFront 或 Lambda@Edge 資源，然後才能刪除服務連結角色。這有助於保護您的 Lambda@Edge 資源，讓您不會移除在存取作用中資源時仍有需要的服務連結角色。

如需服務連結角色的詳細資訊，請參閱[CloudFront 的服務連結角色](security_iam_service-with-iam.md#security_iam_service-with-iam-roles-service-linked)。

### Lambda@Edge 的服務連結角色許可
<a name="slr-permissions-lambda-edge"></a>

Lambda@Edge 使用兩個服務連結角色，分別名為 **AWSServiceRoleForLambdaReplicator** 及 **AWSServiceRoleForCloudFrontLogger**。以下章節說明這些角色的許可。

**Contents**
+ [

#### Lambda Replicator 的服務連結角色許可
](#slr-permissions-lambda-replicator)
+ [

#### CloudFront Logger 的服務連結角色許可
](#slr-permissions-cloudfront-logger)

#### Lambda Replicator 的服務連結角色許可
<a name="slr-permissions-lambda-replicator"></a>

這個服務連結的角色可讓 Lambda 將 Lambda@Edge 函數複製到 AWS 區域。

AWSServiceRoleForLambdaReplicator 服務連結角色信任 `replicator.lambda.amazonaws.com` 服務來擔任該角色。

角色許可政策允許 Lambda@Edge 在指定資源上完成下列動作：
+ `arn:aws:lambda:*:*:function:*` 的 `lambda:CreateFunction`
+ `arn:aws:lambda:*:*:function:*` 的 `lambda:DeleteFunction`
+ `arn:aws:lambda:*:*:function:*` 的 `lambda:DisableReplication`
+ `all AWS resources` 的 `iam:PassRole`
+  `all AWS resources` 的 `cloudfront:ListDistributionsByLambdaFunction`

#### CloudFront Logger 的服務連結角色許可
<a name="slr-permissions-cloudfront-logger"></a>

此服務連結角色可讓 CloudFront 將日誌檔案推送至您的 CloudWatch，以讓您偵錯 Lambda@Edge 驗證錯誤。

AWSServiceRoleForCloudFrontLogger 服務連結角色信任 `logger.cloudfront.amazonaws.com` 服務來擔任該角色。

角色許可政策允許 Lambda@Edge 在指定 `arn:aws:logs:*:*:log-group:/aws/cloudfront/*` 資源上完成下列動作：
+ `logs:CreateLogGroup` ``
+ `logs:CreateLogStream`
+ `logs:PutLogEvents`

您必須設定許可，允許 IAM 實體 (例如使用者、群組或角色) 刪除 Lambda@Edge 服務連結角色。如需詳細資訊，請參閱 *IAM 使用者指南*中的[服務連結角色許可](https://docs.aws.amazon.com/IAM/latest/UserGuide/using-service-linked-roles.html#service-linked-role-permissions)。

### 建立 Lambda@Edge 的服務連結角色
<a name="create-slr-lambda-edge"></a>

一般而言，您不需要手動建立 Lambda@Edge 的服務連結角色。此服務會在以下情境為您自動建立角色：
+ 您第一次建立觸發條件時，服務會建立 AWSServiceRoleForLambdaReplicator 角色 (如果尚未存在)。這個角色可讓 Lambda 將 Lambda@Edge 函數複寫到 AWS 區域。

  如果您刪除服務連結角色，則當您在分佈中為 Lambda@Edge 新增觸發條件時，將會重新建立此角色。
+ 您更新或建立具有 Lambda@Edge 關聯的 CloudFront 分佈時，服務會建立一個 AWSServiceRoleForCloudFrontLogger 角色 (如果該角色尚不存在)。此角色可讓 CloudFront 將日誌檔案推送至 CloudWatch。

  如果您刪除服務連結角色，當您更新或建立具有 Lambda@Edge 關聯的 CloudFront 分佈時，將會再次建立此角色。

若要手動建立這些服務連結角色，您可以執行下列 AWS Command Line Interface (AWS CLI) 命令：

**建立 AWSServiceRoleForLambdaReplicator 角色**
+ 執行下列命令。

  ```
  aws iam create-service-linked-role --aws-service-name replicator.lambda.amazonaws.com
  ```

**建立 AWSServiceRoleForCloudFrontLogger 角色**
+ 執行下列命令。

  ```
  aws iam create-service-linked-role --aws-service-name logger.cloudfront.amazonaws.com
  ```

### 編輯 Lambda@Edge 服務連結角色
<a name="edit-slr-lambda-edge"></a>

Lambda@Edge 不允許您編輯 AWSServiceRoleForLambdaReplicator 或 AWSServiceRoleForCloudFrontLogger 服務連結角色。在服務建立服務連結角色之後，您將無法變更角色名稱，因為各種實體皆可能會參考角色。不過，您可以使用 IAM 來編輯角色描述。如需詳細資訊，請參閱《*IAM 使用者指南*》中的[編輯服務連結角色](https://docs.aws.amazon.com/IAM/latest/UserGuide/using-service-linked-roles.html#edit-service-linked-role)。

### AWS 區域 支援 Lambda@Edge 服務連結角色
<a name="slr-regions-lambda-edge"></a>

CloudFront 在下列 AWS 區域中支援將服務連結角色用於 Lambda@Edge：
+ 美國東部 (維吉尼亞北部) – `us-east-1`
+ 美國東部 (俄亥俄) – `us-east-2`
+ 美國西部 (加利佛尼亞北部) – `us-west-1`
+ 美國西部 (奧勒岡) – `us-west-2`
+ 亞太地區 (孟買) – `ap-south-1`
+ 亞太地區 (首爾) – `ap-northeast-2`
+ 亞太地區 (新加坡) – `ap-southeast-1`
+ 亞太地區 (雪梨) – `ap-southeast-2`
+ 亞太地區 (東京) – `ap-northeast-1`
+ 歐洲 (法蘭克福) – `eu-central-1`
+ 歐洲 (愛爾蘭) – `eu-west-1`
+ 歐洲 (倫敦) – `eu-west-2`
+ 南美洲 (聖保羅) – `sa-east-1`

# 撰寫及建立 Lambda@Edge 函數
<a name="lambda-edge-create-function"></a>

若要使用 Lambda@Edge，請*撰寫* AWS Lambda 函數的程式碼。為了協助您撰寫 Lambda@Edge 函數，請參閱下列資源：
+  [Lambda@Edge 事件結構說明頁面](lambda-event-structure.md)：瞭解與 Lambda@Edge 搭配使用的事件結構。
+ [Lambda@Edge 範例函數](lambda-examples.md)：範例函數，例如 A/B 測試和產生 HTTP 重新導向。

將 Node.js 或 Python 用於 Lambda@Edge 的程式設計模型和在 AWS 區域中使用 Lambda 的程式設計模型相同。如需詳細資訊，請參閱《*AWS Lambda 開發人員指南*》中的[使用 Node.js 建構 Lambda 函數](https://docs.aws.amazon.com/lambda/latest/dg/lambda-nodejs.html)或[使用 Python 建構 Lambda 函數](https://docs.aws.amazon.com/lambda/latest/dg/lambda-python.html)。

在您的 Lambda@Edge 函數中，包含 `callback` 參數並傳回請求或回應事件的適用物件：
+ **請求事件** – 在回應中包含 `cf.request` 物件。

  如果您要產生回應，請在回應中包含 `cf.response` 物件。如需詳細資訊，請參閱[在請求觸發條件中產生 HTTP 回應](lambda-generating-http-responses.md#lambda-generating-http-responses-in-requests)。
+ **回應事件** – 在回應中包含 `cf.response` 物件。

在您撰寫自己的程式碼或使用其中一個範例後，接著在 Lambda 中建立函數。若要建立函數或編輯現有函數，請參閱下列主題：

**Topics**
+ [

# 建立 Lambda@Edge 函數
](lambda-edge-create-in-lambda-console.md)
+ [

# 編輯 Lambda 函數
](lambda-edge-edit-function.md)

 在 Lambda 中建立函數後，您可以設定 Lambda 根據稱為*觸發*的特定 CloudFront 事件執行函數。如需詳細資訊，請參閱[為 Lambda@Edge 函數新增觸發條件](lambda-edge-add-triggers.md)。

# 建立 Lambda@Edge 函數
<a name="lambda-edge-create-in-lambda-console"></a>

若要設定 AWS Lambda 以執行以 CloudFront 事件為基礎的 Lambda 函數，請遵循此程序。<a name="lambda-edge-create-function-procedure"></a>

**建立 Lambda@Edge 函數**

1. 登入 AWS 管理主控台 並在 https：//[https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/) 開啟 AWS Lambda 主控台。

1. 如果您已擁有一個或多個 Lambda 函數，請選擇 **Create function (建立函數)**。

   如果您未擁有任何函數，請選擇 **Get Started Now (立即開始)**。

1. 在頁面頂端的「區域」清單中，選擇**美國東部 (維吉尼亞北部)**。

1. 使用您自己的程式碼建立函數，或以 CloudFront 藍圖為基礎建立函數。
   + 若要使用自己的程式碼來建立函數，請選擇 **Author from scratch (從頭開始編寫)**。
   + 若要顯示 CloudFront 藍圖清單，請在篩選條件欄位輸入 **cloudfront**，然後選擇 **Enter**。

     如果找到想要使用的藍圖，請選擇該藍圖名稱。

1. 在 **Basic information (基本資訊)** 區段中，指定下列的值：

   1. **名稱**：輸入函數名稱。

   1. **角色**：若要快速開始使用，請選擇**從範本建立新角色**。您也可以選擇**選擇現有角色**或**建立自訂角色**，然後依照提示完成本區段的資訊。

   1. **角色名稱**：輸入角色名稱。

   1. **政策範本**：選擇**基本邊緣 Lambda 許可權限**。

1. 如果您在步驟 4 中選擇 **Author from scratch (從頭開始編寫)**，請跳到步驟 7。

   如果您在步驟 4 選擇藍圖，則可於 **cloudfront** 區段建立一個觸發，其可以將此函數與 CloudFront 分佈和 CloudFront 事件中的快取相關聯。我們建議您在此處選擇 **Remove (移除)**，如此函數在建立時就不會有觸發條件。您可以在稍後新增觸發。
**提示**  
建議在新增觸發條件之前，先對函數進行測試及除錯。如果您現在新增觸發條件，函式會在您建立函式並完成複寫至世界各地的 AWS 位置，並部署對應的分佈時立即執行。

1. 選擇 **Create function (建立函數)**。

   Lambda 會建立兩個版本的函數：\$1LATEST 和 Version 1。您只能編輯 \$1LATEST 版本，但是主控台最初會顯示 Version 1。

1. 若要編輯函數，請選擇該函數 ARN 下方、靠近頁面頂端的 **Version 1 (版本 1)**。接著，在 **Versions (版本)** 索引標籤中，選擇 **\$1LATEST (\$1LATEST)**。(如果離開函數再返回，按鈕的標籤會是 **Qualifiers (修飾詞)**。)

1. 在 **Configuration (組態)** 索引標籤中，選擇適用的 **Code entry type (程式碼項目類型)**。然後遵循提示來編輯或上傳程式碼。

1. 針對 **Runtime (執行時間)**，根據函數的程式碼來選擇值。

1. 在 **Tags (標籤)** 區段中，新增任何適用的標籤。

1. 選擇 **Actions (動作)**，然後選擇 **Publish new version (發佈新版本)**。

1. 輸入函數新版本的說明。

1. 選擇 **Publish (發佈)**。

1. 對函數進行測試與偵錯。如需有關在 Lambda 主控台進行測試的詳細資訊，請參閱《*AWS Lambda 開發人員指南*》中的[使用主控台調用 Lambda 函數](https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html#get-started-invoke-manually)。

1. 當您準備好將函數用於 CloudFront 事件執行時，請發佈另一個版本，並編輯該函數以新增觸發。如需詳細資訊，請參閱[為 Lambda@Edge 函數新增觸發條件](lambda-edge-add-triggers.md)。

# 編輯 Lambda 函數
<a name="lambda-edge-edit-function"></a>

建立 Lambda@Edge 函數之後，您可以使用 Lambda 主控台編輯該函數。

**備註**  
原始版本標記為 \$1LATEST。
您只能編輯 \$1LATEST 版本。
每次您編輯 \$1LATEST 版本時，必須發佈新的編號版本。
您無法為 \$1LATEST 建立觸發。
當您發佈新函數版本時，Lambda 不會自動將觸發條件由前一個版本複製到新版本。您必須為新版本重新產生觸發。
當您新增 CloudFront 事件觸發條件到函數時，如果針對相同分佈、快取行為及事件之較早版本的相同函數已具有觸發條件，則 Lambda 會刪除較早版本的觸發條件。
在您對 CloudFront 分佈進行例如新增觸發等更新後，必須等待變更傳播到節點，您由觸發條件所指定的函數才可運作。<a name="lambda-edge-edit-function-procedure"></a>

**若要編輯 Lambda 函數**

1. 登入 AWS 管理主控台 並在 https：//[https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/) 開啟 AWS Lambda 主控台。

1. 在頁面頂端的「區域」清單中，選擇**美國東部 (維吉尼亞北部)**。

1. 在函數清單上選取函數名稱。

   根據預設，主控台會顯示 \$1LATEST 版本。您可以檢視較早的版本 (選擇 **Qualifiers (修飾詞)**)，但是只能編輯 \$1LATEST。

1. 在**程式碼**索引標籤上，針對**程式碼項目類型**，選擇在瀏覽器中編輯程式碼、上傳 .zip 檔案，或從 Amazon S3 上傳檔案。

1. 選擇 **Save (儲存)** 或 **Save and test (儲存並測試)**。

1. 選擇 **Actions (動作)**，然後選擇 **Publish new version (發佈新版本)**。

1. 在 **Publish new version from \$1LATEST (從 \$1LATEST 發佈新版本)** 對話方塊中，輸入新版本的說明。此說明會與自動產生的版本編號一起顯示在版本清單中。

1. 選擇 **Publish (發佈)**。

   新版本會自動成為最新版本。版本編號會顯示於頁面左上角的**版本**按鈕上。
**注意**  
如果您尚未為函數新增觸發條件，請參閱 [為 Lambda@Edge 函數新增觸發條件](lambda-edge-add-triggers.md)。

1. 選擇 **Triggers (觸發條件)** 索引標籤。

1. 選擇 **Add trigger (新增觸發條件)**。

1. 在**新增觸發**對話方塊，選擇虛線方塊，再選擇 **CloudFront**。
**注意**  
如果您已為函數建立一或多個觸發條件，則 CloudFront 為預設服務。

1. 指定下列值，以指示您希望 Lambda 函數在何時執行。

   1. **分佈 ID**：選擇您要為其新增觸發的分佈 ID。

   1. **快取行為**：選擇快取行為，透過該行為指定您要執行函數的物件。

   1. **CloudFront 事件**：選擇促使函數執行的 CloudFront 事件。

   1. **啟用觸發條件及複寫**：選取此核取方塊，讓 Lambda 將函數複寫到全球的 AWS 區域 。

1. 選擇**提交**。

1. 若要為此函數新增更多觸發，請重複操作步驟 10 到 13。

如需更多資訊瞭解在 Lambda 主控台中測試和偵錯函數，請參閱《*AWS Lambda 開發人員指南*》中的[使用主控台調用 Lambda 函數](https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html#get-started-invoke-manually)。

當您準備好將函數用於 CloudFront 事件執行時，請發佈另一個版本，並編輯該函數以新增觸發。如需詳細資訊，請參閱[為 Lambda@Edge 函數新增觸發條件](lambda-edge-add-triggers.md)。

# 為 Lambda@Edge 函數新增觸發條件
<a name="lambda-edge-add-triggers"></a>

Lambda@Edge 觸發器是指 CloudFront 分佈、快取行為及促使函數執行的事件的組合。例如，您可以建立觸發條件，在 CloudFront 收到檢視器針對您為分佈設定的特定快取行為提出的請求時，執行函數。您可以指定一或多個 CloudFront 觸發條件。

**提示**  
當您建立 CloudFront 分佈，您需要指定設定來告知 CloudFront 如何在接收不同的請求時回應。預設設定稱為分佈的*預設快取行為*。您可以設定其他快取行為來定義 CloudFront 在特定情況下回應的方式，例如在接收請求特定的檔案類型的請求時。如需詳細資訊，請參閱[快取行為設定](DownloadDistValuesCacheBehavior.md)。

您首次建立 Lambda 函數時，只能指定*一個*觸發條件。您之後可以使用以下方式將更多觸發條件新增到相同函數：使用 Lambda 主控台或在 CloudFront 主控台上編輯分佈。
+ 如果您要將更多觸發條件新增到同一 CloudFront 分佈的函數中，Lambda 主控台將非常有效。
+ 如果要為多個分佈新增觸發條件，使用 CloudFront 主控台較適合，因為這樣更易於找到您要更新的分佈。您也可以同時更新其他的 CloudFront 設定。

**Topics**
+ [

# 可以觸發 Lambda@Edge 函數的 CloudFront 事件
](lambda-cloudfront-trigger-events.md)
+ [

# 選擇要觸發函數的事件
](lambda-how-to-choose-event.md)
+ [

# 新增觸發條件到 Lambda@Edge 函數
](lambda-edge-add-triggers-console.md)

# 可以觸發 Lambda@Edge 函數的 CloudFront 事件
<a name="lambda-cloudfront-trigger-events"></a>

對於 Amazon CloudFront 分佈中的每個快取行為，您可以新增最多四個觸發條件 (關聯)，以便在特定 CloudFront 事件發生時執行 Lambda 函數。CloudFront 觸發條件可以根據四個 CloudFront 事件之一，如下圖所示。

![\[概念圖：針對與 CloudFront 整合的 Lambda 函數，顯示 CloudFront 如何觸發事件。\]](http://docs.aws.amazon.com/zh_tw/AmazonCloudFront/latest/DeveloperGuide/images/cloudfront-events-that-trigger-lambda-functions.png)


可用於觸發 Lambda@Edge 函數的 CloudFront 事件如下：

**檢視者請求**  
函數會在 CloudFront 接獲檢視器請求時執行，然後再檢視請求的物件是否位在 CloudFront 快取。  
函數不在以下情況執行：  
+ 擷取自訂錯誤頁面時。
+ CloudFront 自動重新導向 HTTP 請求到 HTTPS ([檢視器通訊協定政策](DownloadDistValuesCacheBehavior.md#DownloadDistValuesViewerProtocolPolicy) 的值為**重新導向 HTTP 到 HTTPS** 時)。

**原始伺服器請求**  
函數*只*在 CloudFront 將請求轉傳到您的原始伺服器時執行。如果請求的物件在 CloudFront 快取中，則不會執行該函數。

**原始伺服器回應**  
函數會在 CloudFront 接獲原始伺服器回應後執行，然後才快取回應中的物件。請注意，即使原始伺服器傳回錯誤，函數仍會執行。  
函數不在以下情況執行：  
+ 當請求的檔案在 CloudFront 快取中，並且未過期。
+ 當回應是從原始伺服器請求事件觸發的函數所產生。

**檢視者回應**  
函數會在請求的檔案傳回給檢視器之前執行。請注意，無論檔案是否已在 CloudFront 快取中，函數皆會執行。  
函數不在以下情況執行：  
+ 當原始伺服器傳回 HTTP 狀態碼 400 或更高版本。
+ 當傳回自訂錯誤頁面。
+ 當回應是被檢視器請求事件觸發的函數所產生。
+ CloudFront 自動重新導向 HTTP 請求到 HTTPS ([檢視器通訊協定政策](DownloadDistValuesCacheBehavior.md#DownloadDistValuesViewerProtocolPolicy) 的值為**重新導向 HTTP 到 HTTPS** 時)。

當您新增多個觸發條件至相同的快取行為時，可將其用於針對每個觸發條件執行相同或不同的函數。您也可以將相同的函數與一個以上的分佈建立關聯。

**注意**  
CloudFront 事件觸發執行 Lambda 函數時，該函數必須*先*完成，CloudFront 才能繼續執行。  
例如，當 Lambda 函數是由 CloudFront 檢視器請求事件觸發時，CloudFront 在 Lambda 函數執行完畢之前不會將回應傳回給檢視器或轉送請求給原始伺服器。  
這表示觸發 Lambda 函數的每個請求都會增加請求延遲，因此您會希望函數執行速度越快越好。

# 選擇要觸發函數的事件
<a name="lambda-how-to-choose-event"></a>

當您決定要使用哪個 CloudFront 事件來觸發 Lambda 函數，請考慮以下資訊：

**我希望 CloudFront 快取由 Lambda 函數更改的物件**  
如果要快取由 Lambda 函數修改的物件，以便讓 CloudFront 在下次收到請求時能夠從邊緣節點提供物件，請使用*原始伺服器請求*或*原始伺服器回應*事件。  
這樣可以降低原始伺服器的負載，減少後續請求的延遲，並降低叫用 Lambda@Edge 在後續請求的費用。  
例如，如果您想要新增、移除或變更傳回原始伺服器的物件標頭，且您希望 CloudFront 快取結果，請使用原始伺服器的回應事件。

**我希望函數執行每個請求**  
如果要讓函數執行每個 CloudFront 收到用於分佈的請求，請使用*檢視器請求*或*檢視器回應*事件。  
原始伺服器請求與原始伺服器回應事件，僅會在當請求的物件未快取於節點以及 CloudFront 轉送請求至原始伺服器時發生。

**我希望函數變更快取金鑰**  
如欲變更快取基礎值，請使用*檢視器請求*事件。  
例如，若函數變更其 URL 以包含語言縮寫在路徑中 (例如，因為使用者從下拉式清單中選擇其語言)，請使用檢視器請求事件：  
+ **檢視器請求中的 URL** – https：//https://example.com/en/index.html
+ **當請求來自德國境內 IP 位址時的 URL** – https://example.com/de/index.html
如果您快取根據 Cookie 或請求標頭，也可以使用檢視器請求事件。  
如果函數變更 Cookie 或標頭，可設定 CloudFront 轉送請求適用的部分到原始伺服器。如需詳細資訊，請參閱下列主題：  
+ [根據 Cookie 快取內容](Cookies.md)
+ [根據請求標頭快取內容](header-caching.md)

**函數會影響原始伺服器回應**  
如果您希望依影響原始伺服器回應的方法變更請求，請使用*原始伺服器請求*事件。  
一般而言，大多數檢視器請求事件不會轉送至原始伺服器。CloudFront 會使用已在邊緣快取中的物件回應請求。如果函數依原始伺服器請求事件變更請求，CloudFront 會快取對變更的原始伺服器請求的回應。

# 新增觸發條件到 Lambda@Edge 函數
<a name="lambda-edge-add-triggers-console"></a>

您可以使用 AWS Lambda 主控台或 Amazon CloudFront 主控台，將觸發條件新增至 Lambda@Edge 函數。

**重要**  
您只能為編號的函數版本 (而非 **\$1LATEST**) 建立觸發條件。

------
#### [ Lambda console ]<a name="lambda-edge-add-triggers-procedure"></a>

**將 CloudFront 事件的觸發條件新增至 Lambda@Edge 函數**

1. 登入 AWS 管理主控台 並在 https：//[https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/) 開啟 AWS Lambda 主控台。

1. 在頁面頂端的「區域」清單中，選擇**美國東部 (維吉尼亞北部)**。

1. 在 **Functions (函數)** 頁面上，選擇您要為其新增觸發條件的函數名稱。

1. 在**函數概觀**頁面上，選擇**版本**索引標籤。

1. 選擇您要為其新增觸發的版本。

   選擇版本之後，按鈕的名稱會變更為 **Version: \$1LATEST (版本：\$1LATEST)** 或 **Version: (版本：)** *版本編號*。

1. 選擇 **Triggers (觸發條件)** 索引標籤。

1. 選擇 **Add trigger (新增觸發條件)**。

1. 針對**觸發組態**選擇**選取來源**，輸入 **cloudfront**，然後選擇 **CloudFront**。
**注意**  
如果您已經建立一或多個觸發，則 CloudFront 為預設服務。

1. 指定下列值，以指示您希望 Lambda 函數在何時執行。

   1. **分佈**：選擇您要為其新增觸發的分佈。

   1. **快取行為**：選擇快取行為，透過該行為指定您要執行函數的物件。
**注意**  
如果您為快取行為指定 `*`，Lambda 函式則會部署至預設的快取行為。

   1. **CloudFront 事件**：選擇促使函數執行的 CloudFront 事件。

   1. **包含本文**：如果想要存取您函數中的請求本體，請勾選此核取方塊。

   1. **確認部署到 Lambda@Edge**：選取此核取方塊，以便讓 AWS Lambda 將函數複寫至全球 AWS 區域 。

1. 選擇**新增**。

   將已更新的 CloudFront 分佈部署時，函數便開始針對指定的 CloudFront 事件處理請求。若要判斷是否已部署分佈，請在導覽窗格中選擇 **Distributions (分佈)**。部署分佈時，該分佈**狀態**欄中的值，會從**正在部署**變更為部署的日期與時間。

------
#### [ CloudFront console ]<a name="lambda-create-functions-add-triggers-cloudfront-console-procedure"></a>

**將 CloudFront 事件的觸發條件新增至 Lambda@Edge 函數**

1. 取得您希望新增觸發的 Lambda 函數的 ARN：

   1. 登入 AWS 管理主控台 並在 https：//[https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/) 開啟 AWS Lambda 主控台。

   1. 在頁面頂端的區域清單中，選擇**美國東部 (維吉尼亞北部)**。

   1. 在函數清單上，選取您想要為其新增觸發的函數名稱。

   1. 在**函數概觀**頁面選擇**版本**索引標籤，然後選擇您想要為其新增觸發條件的編號版本。

   1. 選擇**複製 ARN** 按鈕，將 ARN 複製到剪貼簿。Lambda 函數的 ARN 如下所示：

      `arn:aws:lambda:us-east-1:123456789012:function:TestFunction:2`

      最後面的數字 (此範例中為 **2 (2)**) 是該函數的版本編號。

1. 在 [https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home) 開啟 CloudFront 主控台。

1. 在分佈清單中，選取您想要為其新增觸發的分佈 ID。

1. 選擇 **Behaviors (動作)** 索引標籤。

1. 選擇您想要新增觸發條件的快取動作，然後選擇**編輯**。

1. 對於**函數關聯**，請在**函數類型**清單中選擇 **Lambda@Edge**，作為您希望函數執行的時間點：針對檢視器請求、檢視器回應、原始伺服器請求或原始伺服器回應。

   如需詳細資訊，請參閱[選擇要觸發函數的事件](lambda-how-to-choose-event.md)。

1. 在**函數 ARN / 名稱**文字方塊中，貼上所選事件發生時您想要執行 Lambda 函數的 ARN。這是您從 Lambda 主控台複製的值。

1. 如果想要存取您函數中的請求本文，請勾選**包含本文**。

   如果您只想要替換請求本體，就不需要選取此選項。

1. 若要針對更多事件類型執行同一個函數，請重複步驟 6 和 7。

1. 選擇**儲存變更**。

1. 若要為此分佈新增更多快取行為的觸發，請重複步驟 5 到 10。

   將已更新的 CloudFront 分佈部署時，函數便開始針對指定的 CloudFront 事件處理請求。若要判斷是否已部署分佈，請在導覽窗格中選擇 **Distributions (分佈)**。部署分佈時，該分佈**狀態**欄中的值，會從**正在部署**變更為部署的時間與日期。

------

# 測試和偵錯 Lambda@Edge 函數
<a name="lambda-edge-testing-debugging"></a>

單獨測試您 Lambda@Edge 函數的程式碼，和進行整合測試，這些動作非常地重要，前者可確保函數能夠完成預期的任務，後者則可確保函數能夠正確地搭配 CloudFront 使用。

在整合測試期間，或是在已經部署函數之後，您可能會需要進行 CloudFront 錯誤的偵錯，例如 HTTP 5xx 錯誤。錯誤可能是從 Lambda 函數傳回的無效回應、觸發函數時的執行錯誤，或是由於 Lambda 服務進行調節所產生的錯誤。本主題中的段落，說明了用來判斷是哪些故障類型造成問題的策略，以及您可採取的問題修正步驟。

**注意**  
當您在疑難排解錯誤時檢閱 CloudWatch 日誌檔案或指標時，請注意這些檔案或指標會顯示在最 AWS 區域 接近函數執行位置的位置。因此，舉例來說，如果您擁有網站或 Web 應用程式，其使用者位於英國，而您擁有與分佈相關聯的 Lambda 函數，則您必須變更區域，才能檢視倫敦 AWS 區域的 CloudWatch 指標或日誌檔案。如需詳細資訊，請參閱[判斷 Lambda@Edge 區域](#lambda-edge-testing-debugging-determine-region)。

**Topics**
+ [

## 測試您的 Lambda@Edge 函數
](#lambda-edge-testing-debugging-test-function)
+ [

## 在 CloudFront 中識別 Lambda@Edge 函數錯誤
](#lambda-edge-identifying-function-errors)
+ [

## 針對無效的 Lambda@Edge 函數回應 (驗證錯誤) 進行疑難排解
](#lambda-edge-testing-debugging-troubleshooting-invalid-responses)
+ [

## 進行 Lambda@Edge 函數執行錯誤的疑難排解
](#lambda-edge-testing-debugging-execution-errors)
+ [

## 判斷 Lambda@Edge 區域
](#lambda-edge-testing-debugging-determine-region)
+ [

## 判斷您的帳戶是否推送日誌到 CloudWatch
](#lambda-edge-testing-debugging-cloudwatch-logs-enabled)

## 測試您的 Lambda@Edge 函數
<a name="lambda-edge-testing-debugging-test-function"></a>

測試您的 Lambda 函數包括兩個步驟：獨立測試和整合測試。

**測試獨立的功能**  
在將您的 Lambda 函數新增到 CloudFront 之前，請務必先使用 CloudFront 主控台中的測試函數，或利用其他的方法，來進行函數的測試。如需有關在 Lambda 主控台進行測試的詳細資訊，請參閱《*AWS Lambda 開發人員指南*》中的[使用主控台調用 Lambda 函數](https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html#get-started-invoke-manually)。

**在 CloudFront 中測試函數的運作**  
完成整合測試非常地重要，這項測試會建立您的函數與分佈的關聯，並根據 CloudFront 事件執行。請務必以正確的事件觸發函數，並傳回對 CloudFront 有效且正確的回應。例如請確定事件的結構正確、只包含有效的標頭等，以此類推。  
當您在 Lambda 主控台中，重複地進行函數的整合測試時，請參閱 Lambda@Edge 教學課程中的步驟，以修改程式碼，或變更呼叫函數的 CloudFront 觸發條件。例如，請確定您使用函式的編號版本，如教學課程的這項步驟中所述：[步驟 4：新增一個 CloudFront 觸發條件來執行該函數](lambda-edge-how-it-works-tutorial.md#lambda-edge-how-it-works-tutorial-add-trigger)。  
在變更和部署函數時，請注意，更新的函數與 CloudFront 觸發條件複寫到所有區域時，將需要幾分鐘的時間。這通常需要幾分鐘的時間，但最多可能需要 15 分鐘。  
若要檢查複寫是否已完成，請移至 CloudFront主控台並檢視您的分佈。  

**如欲檢查您的複寫是否已完成部署**

1. 在 [https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home) 中開啟 CloudFront 主控台。

1. 選擇分佈名稱。

1. 檢查分佈的狀態是否已從 **In Progress (進行中)** 變回 **Deployed (已部署)**，這表示您的函數已複寫完成。接著，請依照下一節的步驟來驗證函數是否正常運作。
請注意，在主控台中進行的測試只會驗證您的函數邏輯，並不會套用 Lambda@Edge 特定的服務配額 (先前稱為限制)。

## 在 CloudFront 中識別 Lambda@Edge 函數錯誤
<a name="lambda-edge-identifying-function-errors"></a>

確認您的函數邏輯可正常運作之後，當函數在 CloudFront 中執行時，仍然可能會出現 HTTP 5xx 錯誤。有各種原因可能傳回 HTTP 5xx 錯誤，包括 Lambda 函數錯誤或 CloudFront 中的其他問題。
+ 如果您使用 Lambda@Edge 函數，您可以使用 CloudFront 主控台中的圖形以協助追蹤造成錯誤的原因，然後試圖修正該錯誤。例如，您可以檢視 HTTP 5xx 錯誤是否由 CloudFront 或 Lambda 函數造成，然後針對特定函數檢視相關的日誌檔案以調查該問題。
+ 若要排除 CloudFront 中的一般性 HTTP 錯誤，請參閱下列主題中的故障診斷步驟：[對 CloudFront 中的錯誤回應狀態碼進行疑難排解](troubleshooting-response-errors.md)。

### 導致 CloudFront 中 Lambda@Edge 函數錯誤的原因
<a name="lambda-edge-testing-debugging-function-errors"></a>

Lambda 函數造成 HTTP 5xx 錯誤的原因有許多種，您應該依據錯誤的類型採取相應的疑難排解步驟。錯誤分類如下：

**Lambda 函數執行錯誤**  
如果因為函數中存在未處理的例外狀況，或程式碼中出現錯誤，而使得 CloudFront 未獲得 Lambda 的回應，會造成執行錯誤。例如，如果程式碼包含回呼 (錯誤)。

**無效的 Lambda 函數回應會傳回至 CloudFront**  
函數執行後，CloudFront 會收到來自 Lambda 的回應。如果回應的物件結構不符合 [Lambda@Edge 事件結構說明頁面](lambda-event-structure.md)，或回應中包含無效的標頭或其他無效的欄位，系統會傳回錯誤。

**由於 Lambda 服務配額 (以前稱為限制)，CloudFront 中的執行會受到調節**  
Lambda 服務會在各區域中調節執行作業，並在您超出配額時傳回錯誤。如需詳細資訊，請參閱[Lambda@Edge 的配額](cloudfront-limits.md#limits-lambda-at-edge)。

### 如何判斷故障的類型
<a name="lambda-edge-testing-debugging-failure-type"></a>

為了在您偵錯和解決 CloudFront 傳回的錯誤時協助您決定處理重點，找出為何 CloudFront 會傳回 HTTP 錯誤的原因是很有幫助的。若要開始使用，您可以使用 AWS 管理主控台上 CloudFront 主控台的 **Monitoring** (監控) 區段中提供的圖表。如需在 CloudFront 主控台的**監控**區段檢視圖表的詳細資訊，請參閱[使用 Amazon CloudWatch 監控 CloudFront 指標](monitoring-using-cloudwatch.md)。

以下圖表在您追縱原始伺服器或 Lambda 函數是否傳回錯誤時特別實用，當錯誤是由於 Lambda 函數造成時，也可縮小問題的類型。

**錯誤率圖表**  
在每一個分佈的 **Overview (概觀)** 標籤上，您可以檢視的其中一個圖表就是 **Error rates (錯誤率)** 圖表。此圖表顯示錯誤率佔進入您分配的請求總數的百分比。此圖表顯示總錯誤率，總共 4xx 個錯誤、總共 5xx 個錯誤，以及總共 5xx 個 Lambda 函數的錯誤。根據錯誤類型和磁碟區，您可以採取步驟以針對原因進行調查和故障診斷。  

![\[CloudFront 分佈的錯誤率圖形\]](http://docs.aws.amazon.com/zh_tw/AmazonCloudFront/latest/DeveloperGuide/images/Distribution-error-rate-pct-full.png)

+ 如果您看到 Lambda 錯誤，您可以透過查看該函數傳回的特定錯誤類型，以進一步進行調查。**Lambda@Edge 錯誤**標籤包含了依類型分類的函數錯誤圖表，可協助您找出特定函數的問題。
+ 如果您看到 CloudFront 錯誤，您可以進行故障排除並解決原始伺服器錯誤或變更您的 CloudFront 組態。如需詳細資訊，請參閱[對 CloudFront 中的錯誤回應狀態碼進行疑難排解](troubleshooting-response-errors.md)。

**執行錯誤和無效函數回應圖表**  
**Lambda@Edge 錯誤**標籤包含針對特定分佈 (依類型) 分類 Lambda@Edge 錯誤的圖表。例如一個圖表會依 AWS 區域顯示所有執行錯誤。  
為了讓您更輕鬆地對問題進行故障排除，您可以依區域開啟和檢驗特定函數的日誌檔案，以尋找特定問題。  

**依區域檢視特定函數的日誌檔案**

1. 在 **Lambda@Edge 錯誤**索引標籤的**關聯 Lambda@Edge 函數**下，選擇函數名稱，然後選擇**檢視指標**。

1. 接著在出現您函數名稱的頁面上，於右上角選擇**檢視函數日誌**，然後選擇區域。

   例如，如果您在美國西部 (奧勒岡) 區域的**錯誤**圖表中看到問題，請從下拉式清單選擇該區域。隨即開啟 Amazon CloudWatch 主控台。

1. 在該區域 CloudWatch 主控台的**日誌串流**下，選擇日誌串流以檢視函數事件。
此外，請閱讀此章的下列各節，以了解有關故障排除和修復錯誤的更多建議。

**調節圖表**  
**Lambda@Edge 錯誤**標籤也包含**調節**圖表。有時，如果您到達區域並行數量配額 (先前稱為限制)，則 Lambda 服務會依每一區域為基礎調節您的函數呼叫。如果出現超過限制錯誤，表示您的函數已到達 Lambda 服務對「區域」中的執行作業所施加的配額。如需詳細資訊，包括如何請求提高配額，請參閱[Lambda@Edge 的配額](cloudfront-limits.md#limits-lambda-at-edge)。  

![\[Lambda@Edge 函數執行的限流圖表。\]](http://docs.aws.amazon.com/zh_tw/AmazonCloudFront/latest/DeveloperGuide/images/Lambda-throttles-page.png)


如需如何使用此資訊進行 HTTP 錯誤故障診斷的詳細資訊，請參閱[在 AWS上針對您的內容交付執行偵錯的四個步驟](https://aws.amazon.com/blogs/networking-and-content-delivery/four-steps-for-debugging-your-content-delivery-on-aws/)。

## 針對無效的 Lambda@Edge 函數回應 (驗證錯誤) 進行疑難排解
<a name="lambda-edge-testing-debugging-troubleshooting-invalid-responses"></a>

如果您找到的問題是 Lambda 驗證錯誤，表示您的 Lambda 函數將無效的回應傳回給 CloudFront。請依照本段落中的指引，採取步驟來審視您的函數，並確認回應符合 CloudFront 的請求。

CloudFront 會以兩種方式驗證來自 Lambda 函數的回應：
+ **Lambda 回應必須符合所請求的物件結構。**錯誤的物件結構範例包括：無法剖析的 JSON、遺漏必要的欄位，以及在回應中包含無效的物件。如需更多資訊，請參閱[Lambda@Edge 事件結構說明頁面](lambda-event-structure.md)。
+ **回應必須只包含有效的物件值。**如果回應中包含有效的物件，但是具有不支援的值，將會發生錯誤。此種情況的範例包括：新增或更新被列入不允許或唯讀的標頭 (請參閱 [對邊緣函數的限制](edge-functions-restrictions.md))、超過內文大小的上限 (請參閱 Lambda@Edge [錯誤](lambda-generating-http-responses.md#lambda-generating-http-responses-errors) 主題中的*對所產生回應的大小限制*)，以及無效的字元或值 (請參閱 [Lambda@Edge 事件結構說明頁面](lambda-event-structure.md))。

當 Lambda 傳回無效回應至 CloudFront 時，會將錯誤訊息寫入日誌檔案，而 CloudFront 會在執行 Lambda 函數的區域中推送至 CloudWatch。出現無效的回應時，預設的動作是將日誌檔案傳送到 CloudWatch。不過，如果在函數發布之前，就已建立 Lambda 函數與 CloudFront 的關聯，則可能不會針對您的函數啟用這項預設動作。如需詳細資訊，請參閱本主題稍後的*判斷您的帳戶是否推送日誌到 CloudWatch*。

CloudFront 會將日誌檔案推送到對應您函數執行所在位置的區域 (在與您的分佈具有關聯的日誌群組中)。日誌群組具有下列格式：`/aws/cloudfront/LambdaEdge/DistributionId`，其中 *DistributionId* 是您分佈的 ID。若要判斷您可以在哪個區域中找到 CloudWatch 日誌檔案，請參閱本主題稍後的*判斷 Lambda@Edge 區域*。

如果錯誤是可重現的，您可以建立新的請求來造成錯誤，然後在失敗的 CloudFront 回應 (`X-Amz-Cf-Id` 標頭) 中找到該請求的 ID，然後在日誌檔案中找出單一錯誤。日誌檔案記錄所包含的資訊，可協助您找出傳回錯誤的原因，也可以列出對應的 Lambda 請求 ID，來讓您針對單一請求的範圍，分析錯誤的根本原因。

如果錯誤是間歇性出現，您可以利用 CloudFront 存取日誌，針對失敗的請求找出其請求 ID，然後搜尋 CloudWatch Logs，尋找對應的錯誤訊息。如需詳細資訊，請參閱先前的段落*判斷故障的類型*。

## 進行 Lambda@Edge 函數執行錯誤的疑難排解
<a name="lambda-edge-testing-debugging-execution-errors"></a>

如果問題是 Lambda 的執行錯誤，那麼建立 Lambda 函數的記錄陳述式、將訊息寫入 CloudWatch 日誌檔案 (此日誌檔案會監控您的函數在 CloudFront 中的執行狀況)，然後判斷函數是否如預期運作，這些動作將會有所幫助。接著，您可以在 CloudWatch 日誌檔案中搜尋這些陳述式，來確認您的函數是否正常運作。

**注意**  
即使您未變更您的 Lambda@ Edge 函數，Lambda 函數執行環境的更新仍會對其造成影響，並因而傳回執行錯誤。如需有關測試和遷移至較新版本的資訊，請參閱 [Lambda AWS 和 AWS Lambda@Edge 執行環境的近期更新。](https://aws.amazon.com/blogs/compute/upcoming-updates-to-the-aws-lambda-execution-environment/)

## 判斷 Lambda@Edge 區域
<a name="lambda-edge-testing-debugging-determine-region"></a>

若要檢視您 Lambda@Edge 函數正在接收流量的區域，請在 AWS 管理主控台上的 CloudFront 主控台檢視函數的指標圖形。系統會顯示每個 AWS 區域的指標。在同一頁面中，您可以選擇一個區域並檢視該區域的日誌檔，以便調查問題。您必須檢閱正確區域中的 CloudWatch 日誌檔案 AWS ，以查看 CloudFront 執行 Lambda 函數時建立的日誌檔案。

如需在 CloudFront 主控台的**監控**區段檢視圖表的詳細資訊，請參閱[使用 Amazon CloudWatch 監控 CloudFront 指標](monitoring-using-cloudwatch.md)。

## 判斷您的帳戶是否推送日誌到 CloudWatch
<a name="lambda-edge-testing-debugging-cloudwatch-logs-enabled"></a>

根據預設，CloudFront 會啟用記錄無效的 Lambda 函式回應，並使用其中一個 [Lambda@Edge 的服務連結角色](lambda-edge-permissions.md#using-service-linked-roles-lambda-edge) 將日誌檔案推送至 CloudWatch。如果您擁有 Lambda@Edge 函數，而在無效的 Lambda 函數回應日誌函數推出之前，您已經將此 Lambda@Edge 函數新增到 CloudFront，而當您後續更新 Lambda@Edge 組態時，就會啟用記錄函數 (例如，透過新增 CloudFront 觸發條件)。

您可以執行下列的動作，來確認您的帳戶已啟用推送日誌檔案到 CloudWatch 的功能：
+ **檢查日誌是否顯示在 CloudWatch 中**：請務必查看執行 Lambda@Edge 函數的區域。如需詳細資訊，請參閱[判斷 Lambda@Edge 區域](#lambda-edge-testing-debugging-determine-region)。
+ **在 IAM 中判斷您的帳戶是否存在相關的服務連結角色**：您的帳戶必須擁有 IAM 角色 `AWSServiceRoleForCloudFrontLogger`。如需有關此角色的詳細資訊，請參閱 [Lambda@Edge 的服務連結角色](lambda-edge-permissions.md#using-service-linked-roles-lambda-edge)。

# 刪除 Lambda@Edge 函數和複本
<a name="lambda-edge-delete-replicas"></a>

您可以只在函數複本遭 CloudFront 刪除時，系統才會刪除 Lambda@Edge 函數。Lambda 函數的複本將在下列情況自動刪除：
+ 當您從所有 CloudFront 分佈移除最後一個函數的關聯後。如果一個以上的分佈使用一個函數，則只有在您從上一個分佈移除函數關聯之後，複本才會刪除。
+ 在您刪除最後一個與函數相關聯的分佈後。

複本一般會在幾個小時內刪除。您無法手動刪除 Lambda@Edge 函數複本。這有助於防止刪除仍在使用中的複本，以免導致錯誤情況。

**警告**  
請勿建置在 CloudFront 之外使用 Lambda@Edge 函數複本的應用程式。當這些複本與分佈的關聯遭到移除，或分佈本身遭到刪除時，這些複本就會隨之刪除。外部應用程式所依據使用的複本可能會在無預警的情況下移除，導致應用程式作業失敗。

**從 CloudFront 分佈刪除 Lambda@Edge 函數關聯**

1. 登入 AWS 管理主控台 ，並在 開啟 CloudFront 主控台[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 選擇具有您要刪除之 Lambda@Edge 函數關聯的分佈 ID。

1. 選擇 **Behaviors (動作)** 索引標籤。

1. 選擇具有您要刪除之 Lambda@Edge 函數關聯的快取行為，然後選擇**編輯**。

1. 在**函數關聯**、**函數類型**下，選擇**沒有關聯**以刪除 Lambda@Edge 函數關聯。

1. 選擇**儲存變更**。

從 CloudFront 分發中刪除 Lambda@Edge 函數關聯之後，您可以選擇性地從 AWS Lambda中刪除 Lambda 函數或函數版本。刪除函數關聯後請等待數小時，以便清除 Lambda@Edge 函數複本。之後，您可以使用 Lambda 主控台、、 AWS CLI Lambda API 或 AWS SDK 刪除函數。

您也可以刪除特定*版本*的 Lambda 函數，前提示該版本未與任何 CloudFront 分佈建立關聯。移除 Lambda 函數版本的所有關聯後，請等待數小時。然後您可以刪除函數版本。

# Lambda@Edge 事件結構說明頁面
<a name="lambda-event-structure"></a>

下列主題說明在觸發時，CloudFront 傳遞到 Lambda@Edge 函數的請求和回應事件物件。

**Topics**
+ [

## 動態原始伺服器選擇
](#lambda-event-content-based-routing)
+ [

## 請求事件
](#lambda-event-structure-request)
+ [

## 回應事件
](#lambda-event-structure-response)

## 動態原始伺服器選擇
<a name="lambda-event-content-based-routing"></a>

您可以根據路徑和請求物件名稱 (例如 `images/*.jpg`)，使用[快取行為中的路徑模式](DownloadDistValuesCacheBehavior.md#DownloadDistValuesPathPattern)將請求路由傳送到原始伺服器。使用 Lambda@Edge，您也可以根據其他特性路由傳送請求到原始伺服器，例如請求標頭中的值。

此動態原始伺服器選擇在很多方面是非常有用的。例如，您可以將請求分佈到各個在不同地理區域的原始伺服器，以協助全域負載平衡。或者，您可以選擇性地路由傳送請求到服務特定函數之不同的原始伺服器：機器人處理、SEO 最佳化、驗證，以此類推。如需示範如何使用此功能的程式碼範例，請參閱[以內容為基礎的動態原始伺服器選擇 - 範例](lambda-examples.md#lambda-examples-content-based-routing-examples)。

在 CloudFront 原始伺服器請求事件中，事件結構中的 `origin` 物件包含了關於該原始伺服器的資訊 (會根據路徑的模式，將請求轉送到自訂此原始伺服器)。您可以更新 `origin` 物件的值，將請求路由傳送到不同的原始伺服器。更新 `origin` 物件時，您不需要定義分佈中的原始伺服器。您也可以使用自訂原始伺服器物件來取代 Amazon S3 原始伺服器物件，反之亦然。不過，您只能為每個請求指定單一原始伺服器；也就是指定自訂原始伺服器或 Amazon S3 原始伺服器，但不能兩者同時指定。

## 請求事件
<a name="lambda-event-structure-request"></a>

下列主題說明 CloudFront 針對[檢視器和原始伺服器請求事件](lambda-cloudfront-trigger-events.md)傳遞到 Lambda 函數的結構。這些範例顯示不含任何主體的 `GET` 請求。在範例之後，會提供檢視器和原始伺服器請求事件中的所有可能欄位清單。

**Topics**
+ [

### 檢視者請求範例
](#example-viewer-request)
+ [

### 原始伺服器請求範例
](#example-origin-request)
+ [

### 請求事件欄位
](#request-event-fields)

### 檢視者請求範例
<a name="example-viewer-request"></a>

下列範例顯示檢視器請求事件物件。

```
{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionDomainName": "d111111abcdef8.cloudfront.net",
          "distributionId": "EDFDVBD6EXAMPLE",
          "eventType": "viewer-request",
          "requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
        },
        "request": {
          "clientIp": "203.0.113.178",
          "headers": {
            "host": [
              {
                "key": "Host",
                "value": "d111111abcdef8.cloudfront.net"
              }
            ],
            "user-agent": [
              {
                "key": "User-Agent",
                "value": "curl/7.66.0"
              }
            ],
            "accept": [
              {
                "key": "accept",
                "value": "*/*"
              }
            ]
          },
          "method": "GET",
          "querystring": "",
          "uri": "/"
        }
      }
    }
  ]
}
```

### 原始伺服器請求範例
<a name="example-origin-request"></a>

下列範例顯示原始伺服器請求事件物件。

```
{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionDomainName": "d111111abcdef8.cloudfront.net",
          "distributionId": "EDFDVBD6EXAMPLE",
          "eventType": "origin-request",
          "requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
        },
        "request": {
          "clientIp": "203.0.113.178",
          "headers": {
            "x-forwarded-for": [
              {
                "key": "X-Forwarded-For",
                "value": "203.0.113.178"
              }
            ],
            "user-agent": [
              {
                "key": "User-Agent",
                "value": "Amazon CloudFront"
              }
            ],
            "via": [
              {
                "key": "Via",
                "value": "2.0 2afae0d44e2540f472c0635ab62c232b.cloudfront.net (CloudFront)"
              }
            ],
            "host": [
              {
                "key": "Host",
                "value": "example.org"
              }
            ],
            "cache-control": [
              {
                "key": "Cache-Control",
                "value": "no-cache"
              }
            ]
          },
          "method": "GET",
          "origin": {
            "custom": {
              "customHeaders": {},
              "domainName": "example.org",
              "keepaliveTimeout": 5,
              "path": "",
              "port": 443,
              "protocol": "https",
              "readTimeout": 30,
              "responseCompletionTimeout": 30,
              "sslProtocols": [
                "TLSv1",
                "TLSv1.1",
                "TLSv1.2"
              ]
            }
          },
          "querystring": "",
          "uri": "/"
        }
      }
    }
  ]
}
```

### 請求事件欄位
<a name="request-event-fields"></a>

請求事件物件資料包含在兩個子物件中：`config` (`Records.cf.config`) 和 `request` (`Records.cf.request`)。下列清單說明每個子物件的欄位。

#### Config 物件中的欄位
<a name="request-event-fields-config"></a>

下列清單說明 `config` 物件 (`Records.cf.config`) 中的欄位。

**`distributionDomainName` (唯讀)**  
與請求相關的分佈網域名稱。

**`distributionID` (唯讀)**  
與請求相關的分佈 ID。

**`eventType` (唯讀)**  
與請求關聯的觸發條件類型：`viewer-request` 或 `origin-request`。

**`requestId` (唯讀)**  
可獨特識別檢視器至 CloudFront 請求的加密字串。此 `requestId` 值也會在 CloudFront 存取記錄中顯示為 `x-edge-request-id`。如需詳細資訊，請參閱[存取日誌 （標準日誌）](AccessLogs.md)及[日誌檔案欄位](standard-logs-reference.md#BasicDistributionFileFormat)。

#### 請求物件中的欄位
<a name="request-event-fields-request"></a>

下列清單說明 `request` 物件 (`Records.cf.request`) 中的欄位。

**`clientIp` (唯讀)**  
提出請求之檢視器的 IP 位址。如果檢視器使用 HTTP Proxy 或負載平衡器傳送請求，此值為代理或負載平衡器的 IP 位址。

** 標頭 (讀取/寫入)**  
請求中的標頭。注意下列事項：  
+ 在 `headers` 物件的金鑰為標準 HTTP 標頭名稱的小寫版本。使用小寫金鑰提供您區分大小寫的標頭值存取權。
+ 每個標頭物件 (例如 `headers["accept"]` 或 `headers["host"]`) 都是鍵值組的陣列。針對已知的標頭，陣列會在請求中包含每個值的一組鍵值組。
+ `key` 包含在 HTTP 請求中出現的標頭名稱 (區分大小寫)，例如 `Host`、`User-Agent`、`X-Forwarded-For`、`Cookie` 等。
+ `value` 包含在 HTTP 請求中出現的標頭值。
+ 當 Lambda 函數新增或修改請求標頭，而您不想要包含標頭 `key` 欄位時，Lambda@Edge 會使用您提供的標頭名稱來自動插入 `key` 標頭。無論您如何設定標頭名稱格式，各部分插入的標頭索引鍵名稱都會自動以大寫開頭，以連字號 (-) 分隔。

  例如，您可以在沒有 `key` 標頭的情況下，新增標頭如下：

  ```
  "user-agent": [
    {
      "value": "ExampleCustomUserAgent/1.X.0"
    }
  ]
  ```

  在此範例中，Lambda@Edge 會自動插入 `"key": "User-Agent"`。
如需標頭使用限制的詳細資訊，請參閱[對邊緣函數的限制](edge-functions-restrictions.md)。

**`method` (唯讀)**  
請求的 HTTP 方法。

**`querystring` (讀取/寫入)**  
請求中的查詢字串 (如果有的話)。如果請求不包含查詢字串，事件物件仍會包含具有空白值的 `querystring`。如需查詢字串的詳細資訊，請參閱[根據查詢字串參數快取內容](QueryStringParameters.md)。

**`uri` (讀取/寫入)**  
請求物件的相對路徑。如果 Lambda 函數修改了 `uri` 值，請注意下列事項：  
+ 全新的 `uri` 值必須以正斜線 (/) 做為開頭。
+ 當函數變更 `uri` 值時，這會變更檢視器請求的物件。
+ 當函數變更 `uri` 值時，並*不會*變更請求的快取行為或請求傳送的目標原始伺服器。

**`body` (讀取/寫入)**  
HTTP 請求的主題。`body` 結構可包含下列欄位：    
**`inputTruncated` (唯讀)**  
布林值旗標，此旗標會指出 Lambda@Edge 是否截斷了本體。如需詳細資訊，請參閱[使用包含內文選項時的要求內文限制](lambda-at-edge-function-restrictions.md#lambda-at-edge-restrictions-request-body)。  
**`action` (讀取/寫入)**  
您想要對本體採取的動作。`action` 的選項如下：  
+ `read-only:` 此為預設值。從 Lambda 函式將回應傳回時，如果 `action` 為唯讀狀態，則 Lambda@Edge 會忽略對 `encoding` 或 `data` 所進行的任何變更。
+ `replace:` 如果想取代傳送到原始伺服器的本體，請指定此選項。  
**`encoding` (讀取/寫入)**  
本體的編碼。當 Lambda@Edge 向 Lambda 函式顯露內文時，會先將內文轉換為 base64-encoding。如果針對 `action` 選擇 `replace` 以取代本體，您可以選擇使用 `base64` (預設) 或 `text` 編碼。如果將 `encoding` 指定為 `base64`，但內文並非有效的 base64，CloudFront 會傳回錯誤。  
**`data` (讀取/寫入)**  
請求本體的內容。

**`origin` (讀取/寫入) (僅限原始伺服器事件)**  
傳送請求的目標原始伺服器。`origin` 結構必須*確切地包含一個原始伺服器*，這可以是自訂原始伺服器或 Amazon S3 原始伺服器。  
根據您指定的原始伺服器類型 (自訂或 Amazon S3 原始伺服器)，您必須在請求中指定下列欄位：    
**`customHeaders` (讀取/寫入) (自訂和 Amazon S3 原始伺服器)**  
(選用) 您可以透過指定的標頭名稱與每個自訂標頭的值對，於請求中包含自訂標頭。您無法新增已列入不允許的標頭，且具有相同名稱的標頭也無法出現在 `Records.cf.request.headers` 中。[請求標頭的相關注意事項](#request-event-fields-request-headers)也適用於自訂標頭。如需詳細資訊，請參閱[CloudFront 無法新增到原始伺服器請求的自訂標頭](add-origin-custom-headers.md#add-origin-custom-headers-denylist)及[對邊緣函數的限制](edge-functions-restrictions.md)。  
**`domainName` (讀取/寫入) (自訂和 Amazon S3 原始伺服器)**  
原始伺服器的網域名稱。網域名稱不能空白。  
+ **適用於自訂原始伺服器**：指定 DNS 網域名稱，例如 `www.example.com`。網域名稱不能包含冒號 (:)，而且不能是 IP 位址。網域名稱長度上限為 253 個字元。
+ **對於 Amazon S3 原始伺服器** — 指定 Amazon S3 儲存貯體的 DNS 網域名稱，例如 `amzn-s3-demo-bucket.s3.eu-west-1.amazonaws.com`. 名稱可以高達 128 個字元，而且必須全部小寫。  
**`path` (讀取/寫入) (自訂和 Amazon S3 原始伺服器)**  
目錄路徑位於需定位內容請求的原始伺服器中。路徑的開頭應為正斜線 (/)，但結尾不應為正斜線 (例如，結尾不應為 `example-path/`)。僅適用於自訂原始伺服器：路徑應為 URL 編碼，且其長度上限為 255 個字元。  
**`keepaliveTimeout` (讀取/寫入) (僅限自訂原始伺服器)**  
CloudFront 在收到上次封包的回應後維持與自訂原始伺服器的連線所需的時間長度 (以秒為單位)。此值必須是介於 1 到 120 之間的數字。  
**`port` (讀取/寫入) (僅限自訂原始伺服器)**  
CloudFront 應在您的自訂原始伺服器連線的連線埠。此連結埠必須是 80、443，或是介於 1024–65535 之間的數字。  
**`protocol` (讀取/寫入) (僅限自訂原始伺服器)**  
CloudFront 在連線到原始伺服器時應使用的連線通訊協定。此值可以為 `http` 或 `https`。  
**`readTimeout` (讀取/寫入) (自訂和 Amazon S3 原始伺服器)**  
CloudFront 在傳送請求給原始伺服器後應等候回應的時間長度 (以秒為單位)。這也會指定 CloudFront 在收到回應封包後與收到下一個封包前，應等候的時間長度。此值必須是介於 1 到 120 之間的數字。  
如果您需要更高的配額，請參閱[每個原始伺服器的回應逾時](cloudfront-limits.md#limits-web-distributions)。  
**`responseCompletionTimeout` (讀取/寫入) (自訂和 Amazon S3 原始伺服器)**  
從 CloudFront 到原始伺服器的請求可以保持開啟並等待回應的時間 (以秒為單位)。如果此時未從原始伺服器收到完整回應，CloudFront 會結束連線。  
`responseCompletionTimeout` 值必須大於或等於 `readTimeout` 的值。如果您將此值設定為 0，則會移除您設定的任何先前值，並返回預設值。您也可以從事件請求中刪除 `responseCompletionTimeout` 欄位來完成此操作。  
**`sslProtocols` (讀取/寫入) (僅限自訂原始伺服器)**  
當在您原始伺服器建立 HTTPS 連線時，CloudFront 可以使用的最低 SSL/TLS 通訊協定。可為以下任何一個值：`TLSv1.2`、`TLSv1.1`、`TLSv1` 或 `SSLv3`。  
**`authMethod` (讀取/寫入) (僅限 Amazon S3 原始伺服器)**  
如果您使用[原始存取身分 (OAI)](private-content-restricting-access-to-s3.md#private-content-restricting-access-to-s3-oai)，請將此欄位設定為 `origin-access-identity`。如果您未使用 OAI，請將其設定為 `none`。如果您將 `authMethod` 設定為 `origin-access-identity`，有幾項要求如下：  
+ 您必須指定 `region` (請參閱下列欄位)。
+ 當您將請求從某個 Amazon S3 原始伺服器變更為其他原始伺服器時，您必須使用相同的 OAI。
+ 當您將請求從某個自訂原始伺服器變更為 Amazon S3 原始伺服器時，您無法使用 OAI。
此欄位不支援[原始存取控制 (OAC)](private-content-restricting-access-to-s3.md)。  
**`region` (讀取/寫入) (僅限 Amazon S3 原始伺服器)**  
Amazon S3 儲存貯體 AWS 的區域。只有在您將 `authMethod` 設定為 `origin-access-identity` 時才需要。

## 回應事件
<a name="lambda-event-structure-response"></a>

下列主題說明 CloudFront 針對[檢視器和原始伺服器回應事件](lambda-cloudfront-trigger-events.md)傳遞到 Lambda 函數的結構。在範例之後，會提供檢視器和原始伺服器回應事件中的所有可能欄位清單。

**Topics**
+ [

### 原始伺服器回應範例
](#lambda-event-structure-response-origin)
+ [

### 檢視者回應範例
](#lambda-event-structure-response-viewer)
+ [

### 回應事件欄位
](#response-event-fields)

### 原始伺服器回應範例
<a name="lambda-event-structure-response-origin"></a>

下列範例顯示原始伺服器回應事件物件。

```
{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionDomainName": "d111111abcdef8.cloudfront.net",
          "distributionId": "EDFDVBD6EXAMPLE",
          "eventType": "origin-response",
          "requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
        },
        "request": {
          "clientIp": "203.0.113.178",
          "headers": {
            "x-forwarded-for": [
              {
                "key": "X-Forwarded-For",
                "value": "203.0.113.178"
              }
            ],
            "user-agent": [
              {
                "key": "User-Agent",
                "value": "Amazon CloudFront"
              }
            ],
            "via": [
              {
                "key": "Via",
                "value": "2.0 8f22423015641505b8c857a37450d6c0.cloudfront.net (CloudFront)"
              }
            ],
            "host": [
              {
                "key": "Host",
                "value": "example.org"
              }
            ],
            "cache-control": [
              {
                "key": "Cache-Control",
                "value": "no-cache"
              }
            ]
          },
          "method": "GET",
          "origin": {
            "custom": {
              "customHeaders": {},
              "domainName": "example.org",
              "keepaliveTimeout": 5,
              "path": "",
              "port": 443,
              "protocol": "https",
              "readTimeout": 30,
              "responseCompletionTimeout": 30,
              "sslProtocols": [
                "TLSv1",
                "TLSv1.1",
                "TLSv1.2"
              ]
            }
          },
          "querystring": "",
          "uri": "/"
        },
        "response": {
          "headers": {
            "access-control-allow-credentials": [
              {
                "key": "Access-Control-Allow-Credentials",
                "value": "true"
              }
            ],
            "access-control-allow-origin": [
              {
                "key": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ],
            "date": [
              {
                "key": "Date",
                "value": "Mon, 13 Jan 2020 20:12:38 GMT"
              }
            ],
            "referrer-policy": [
              {
                "key": "Referrer-Policy",
                "value": "no-referrer-when-downgrade"
              }
            ],
            "server": [
              {
                "key": "Server",
                "value": "ExampleCustomOriginServer"
              }
            ],
            "x-content-type-options": [
              {
                "key": "X-Content-Type-Options",
                "value": "nosniff"
              }
            ],
            "x-frame-options": [
              {
                "key": "X-Frame-Options",
                "value": "DENY"
              }
            ],
            "x-xss-protection": [
              {
                "key": "X-XSS-Protection",
                "value": "1; mode=block"
              }
            ],
            "content-type": [
              {
                "key": "Content-Type",
                "value": "text/html; charset=utf-8"
              }
            ],
            "content-length": [
              {
                "key": "Content-Length",
                "value": "9593"
              }
            ]
          },
          "status": "200",
          "statusDescription": "OK"
        }
      }
    }
  ]
}
```

### 檢視者回應範例
<a name="lambda-event-structure-response-viewer"></a>

下列範例顯示檢視器回應事件物件。

```
{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionDomainName": "d111111abcdef8.cloudfront.net",
          "distributionId": "EDFDVBD6EXAMPLE",
          "eventType": "viewer-response",
          "requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
        },
        "request": {
          "clientIp": "203.0.113.178",
          "headers": {
            "host": [
              {
                "key": "Host",
                "value": "d111111abcdef8.cloudfront.net"
              }
            ],
            "user-agent": [
              {
                "key": "User-Agent",
                "value": "curl/7.66.0"
              }
            ],
            "accept": [
              {
                "key": "accept",
                "value": "*/*"
              }
            ]
          },
          "method": "GET",
          "querystring": "",
          "uri": "/"
        },
        "response": {
          "headers": {
            "access-control-allow-credentials": [
              {
                "key": "Access-Control-Allow-Credentials",
                "value": "true"
              }
            ],
            "access-control-allow-origin": [
              {
                "key": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ],
            "date": [
              {
                "key": "Date",
                "value": "Mon, 13 Jan 2020 20:14:56 GMT"
              }
            ],
            "referrer-policy": [
              {
                "key": "Referrer-Policy",
                "value": "no-referrer-when-downgrade"
              }
            ],
            "server": [
              {
                "key": "Server",
                "value": "ExampleCustomOriginServer"
              }
            ],
            "x-content-type-options": [
              {
                "key": "X-Content-Type-Options",
                "value": "nosniff"
              }
            ],
            "x-frame-options": [
              {
                "key": "X-Frame-Options",
                "value": "DENY"
              }
            ],
            "x-xss-protection": [
              {
                "key": "X-XSS-Protection",
                "value": "1; mode=block"
              }
            ],
            "age": [
              {
                "key": "Age",
                "value": "2402"
              }
            ],
            "content-type": [
              {
                "key": "Content-Type",
                "value": "text/html; charset=utf-8"
              }
            ],
            "content-length": [
              {
                "key": "Content-Length",
                "value": "9593"
              }
            ]
          },
          "status": "200",
          "statusDescription": "OK"
        }
      }
    }
  ]
}
```

### 回應事件欄位
<a name="response-event-fields"></a>

回應事件物件資料包含在三個子物件中：`config` (`Records.cf.config`)、`request` (`Records.cf.request`) 和 `response` (`Records.cf.response`)。如需請求物件中欄位的詳細資訊，請參閱[請求物件中的欄位](#request-event-fields-request)。下列清單說明 `config` 和 `response` 子物件中的欄位。

#### Config 物件中的欄位
<a name="response-event-fields-config"></a>

下列清單說明 `config` 物件 (`Records.cf.config`) 中的欄位。

**`distributionDomainName` (唯讀)**  
與回應關聯的分佈的網域名稱。

**`distributionID` (唯讀)**  
與回應關聯的分佈 ID。

**`eventType` (唯讀)**  
與回應關聯的觸發類型：`origin-response` 或 `viewer-response`。

**`requestId` (唯讀)**  
可獨特識別與此回應相關聯之檢視器至 CloudFront 請求的加密字串。此 `requestId` 值也會在 CloudFront 存取記錄中顯示為 `x-edge-request-id`。如需詳細資訊，請參閱[存取日誌 （標準日誌）](AccessLogs.md)及[日誌檔案欄位](standard-logs-reference.md#BasicDistributionFileFormat)。

#### 回應物件中的欄位
<a name="response-event-fields-response"></a>

下列清單說明 `response` 物件 (`Records.cf.response`) 中的欄位。如需如何使用 Lambda@Edge 函式產生 HTTP 請求的資訊，請參閱[在請求觸發條件中產生 HTTP 回應](lambda-generating-http-responses.md#lambda-generating-http-responses-in-requests)。

**`headers` (讀取/寫入)**  
回應中的標頭。注意下列事項：  
+ 在 `headers` 物件的金鑰為標準 HTTP 標頭名稱的小寫版本。使用小寫金鑰提供您區分大小寫的標頭值存取權。
+ 每個標頭物件 (例如 `headers["content-type"]` 或 `headers["content-length"]`) 都是鍵值組的陣列。針對已知的標頭，陣列會在回應中包含每個值的一組鍵值組。
+ `key` 包含在 HTTP 回應中出現的標頭名稱 (區分大小寫)，例如 `Content-Type`、`Content-Length`、`Cookie` 等。
+ `value` 包含在 HTTP 回應中出現的標頭值。
+ 當 Lambda 函數新增或修改回應標頭，而您不想要包含標頭 `key` 欄位時，Lambda@Edge 會使用您提供的標頭名稱來自動插入 `key` 標頭。無論您如何設定標頭名稱格式，各部分插入的標頭索引鍵名稱都會自動以大寫開頭，以連字號 (-) 分隔。

  例如，您可以在沒有 `key` 標頭的情況下，新增標頭如下：

  ```
  "content-type": [
    {
      "value": "text/html;charset=UTF-8"
    }
  ]
  ```

  在此範例中，Lambda@Edge 會自動插入 `"key": "Content-Type"`。
如需標頭使用限制的詳細資訊，請參閱[對邊緣函數的限制](edge-functions-restrictions.md)。

**`status`**  
回應的 HTTP 狀態碼。

**`statusDescription`**  
回應的 HTTP 狀態說明。

# 使用請求和回應
<a name="lambda-generating-http-responses"></a>

若要使用 Lambda@Edge 請求和回應，請參閱下列主題：

**Topics**
+ [

## 使用 Lambda@Edge 函數搭配原始伺服器容錯移轉
](#lambda-and-origin-failover)
+ [

## 在請求觸發條件中產生 HTTP 回應
](#lambda-generating-http-responses-in-requests)
+ [

## 在原始伺服器回應觸發條件中更新 HTTP 回應
](#lambda-updating-http-responses)
+ [

## 選擇包含本文選項來存取請求本文
](#lambda-include-body-access)

## 使用 Lambda@Edge 函數搭配原始伺服器容錯移轉
<a name="lambda-and-origin-failover"></a>

您可以使用 Lambda@Edge 函數，與您已使用原始伺服器群組來設定的 CloudFront 分佈搭配，例如，針對您設定來確保高可用性的原始伺服器容錯移轉。若要使用 Lambda 函數與原始伺服器群組搭配，請在您建立快取行為時針對原始伺服器群組觸發的原始伺服器請求或原始伺服器回應中指定函數。

如需詳細資訊，請參閱下列內容：
+ **建立原始伺服器群組：**[建立原始伺服器群組](high_availability_origin_failover.md#concept_origin_groups.creating)
+ **原始伺服器容錯移轉如何使用 Lambda@Edge：**[使用原始伺服器容錯移轉與 Lambda@Edge 函數搭配](high_availability_origin_failover.md#concept_origin_groups.lambda)

## 在請求觸發條件中產生 HTTP 回應
<a name="lambda-generating-http-responses-in-requests"></a>

當 CloudFront 收到請求時，您可以使用 Lambda 函數產生 CloudFront 無需轉送回應給原始伺服器，可直接傳回給檢視器的 HTTP 回應。產生 HTTP 回應降低原始伺服器的負載，且通常也可減少檢視器的延遲。

產生 HTTP 回應的一些常見案例包括下列項目：
+ 傳回小型網頁給檢視器
+ 傳回 HTTP 301 或 302 狀態碼來重新導向使用者到另一個網頁
+ 當使用者尚未驗證時，傳回 HTTP 401 狀態碼至檢視器

當下列 CloudFront 事件發生時，Lambda@Edge 函數可以產生 HTTP 回應事件：

**檢視器請求事件**  
當函數被檢視器請求事件觸發時，CloudFront 會將回應傳回檢視器，而且加以不快取。

**原始伺服器請求事件**  
當函數被原始伺服器請求事件觸發時，CloudFront 會檢查先前函數產生的邊緣快取回應。  
+ 如果回應在快取中，系統不會執行該函數，且 CloudFront 會將快取的回應傳回給檢視器。
+ 如果回應不在快取中，會執行該函數，CloudFront 會將回應傳回給檢視器並加以快取。

若要查看產生 HTTP 回應的範本程式碼，請參閱 [Lambda@Edge 範例函數](lambda-examples.md)。您也可以在回應觸發中取代 HTTP 回應。如需詳細資訊，請參閱[在原始伺服器回應觸發條件中更新 HTTP 回應](#lambda-updating-http-responses)。

### 程式設計模型
<a name="lambda-generating-http-responses-programming-model"></a>

本節說明使用 Lambda@Edge 產生 HTTP 回應的程式設計模型。

**Topics**
+ [

#### 回應物件
](#lambda-generating-http-responses-object)
+ [

#### 錯誤
](#lambda-generating-http-responses-errors)
+ [

#### 必要欄位
](#lambda-generating-http-responses-required-fields)

#### 回應物件
<a name="lambda-generating-http-responses-object"></a>

以 `result` 方法的 `callback` 參數傳回的回應，需具備下列結構 (請注意，僅 `status` 欄位為必要)。

```
const response = {
    body: 'content',
    bodyEncoding: 'text' | 'base64',
    headers: {
        'header name in lowercase': [{
            key: 'header name in standard case',
            value: 'header value'
         }],
         ...
    },
    status: 'HTTP status code (string)',
    statusDescription: 'status description'
};
```

回應物件可以包含以下值：

**`body`**  
如有您希望 CloudFront 在產生的回應中傳回的本文。

**`bodyEncoding`**  
您在 `body` 中指定的值的編碼。唯一的有效編碼是 `text` 和 `base64`。如果您在 `response` 物件中加入 `body`，但省略了 `bodyEncoding`，則 CloudFront 會將內文視為文字。  
如果您將 `bodyEncoding` 指定為 `base64` 但本文不是有效的 base64，CloudFront 會傳回錯誤。

**`headers`**  
您希望 CloudFront 於產生的回應中傳回標頭。注意下列事項：  
+ 在 `headers` 物件的金鑰為標準 HTTP 標頭名稱的小寫版本。使用小寫金鑰提供您區分大小寫的標頭值存取權。
+ 每個標頭 (例如 `headers["accept"]` 或 `headers["host"]`) 是一系列的鍵值組。於已知的標頭，陣列在產生的回應中包含每個值的一組鍵值組。
+ `key` (選用) 為在 HTTP 請求中出現的標頭區分大小寫的名稱，例如 `accept` 或 `host`。
+ 指定 `value` 為標頭值。
+ 如果您未加入索引鍵/值組的標頭索引鍵部分，Lambda@Edge 會自動使用您提供的標頭名稱，來插入標頭索引鍵。無論您如何安排標頭格式，各部分插入的標頭索引鍵名稱都會自動以大寫開頭，以連字號 (-) 分隔。

  例如，您可以不用標頭索引鍵，新增標頭如下：`'content-type': [{ value: 'text/html;charset=UTF-8' }]`

  在這個範例中，Lambda@Edge 建立了下列標頭索引鍵：`Content-Type`。
如需標頭使用限制的詳細資訊，請參閱[對邊緣函數的限制](edge-functions-restrictions.md)。

**`status`**  
HTTP 狀態碼 。以字串形式提供狀態碼。CloudFront 使用提供的狀態碼進行以下操作：  
+ 在回應中傳回
+ 當原始伺服器請求事件觸發的函數產生回應時，在 CloudFront 邊緣快取中快取。
+ 登入 CloudFront [存取日誌 （標準日誌）](AccessLogs.md)
如果 `status` 值不在 200 與 599 之間，CloudFront 會將錯誤傳回至檢視器。

**`statusDescription`**  
您想要 CloudFront 在回應中傳回的描述能搭配 HTTP 狀態碼。您不需要使用標準的描述，例如 HTTP 200 狀態碼為 `OK`。

#### 錯誤
<a name="lambda-generating-http-responses-errors"></a>

以下是產生的 HTTP 回應的可能錯誤。

**回應包含本文與指定 204 (無內容) 狀態**  
當函數是由檢視器請求觸發，當下列二者為 true，CloudFront 會傳回 HTTP 502 狀態碼 (無效的閘道) 至檢視器：  
+ `status` 的值是 204 (無內容)
+ 回應包含 `body` 的值
這是因為 Lambda@Edge 強加於 RFC 2616 中選用的限制，也就是 `HTTP 204` 回應不需要包含訊息本文。

**已產生回應的大小限制**  
由 Lambda 函數產生的回應大小上限取決於觸發函數的事件：  
+ **檢視器請求事件** – 40 KB
+ **原始伺服器請求事件** – 1 MB
如果回應超過允許的大小，CloudFront 會將 HTTP 502 狀態碼 (無效的閘道) 傳回檢視器。

#### 必要欄位
<a name="lambda-generating-http-responses-required-fields"></a>

`status` 欄位是必要的。

所有其他欄位是選用的。

## 在原始伺服器回應觸發條件中更新 HTTP 回應
<a name="lambda-updating-http-responses"></a>

當 CloudFront 從原始伺服器收到 HTTP 回應時，如果某個原始伺服器回應觸發關聯到快取行為，您可以修改 HTTP 回應以覆寫原始伺服器傳回的回應。

更新 HTTP 回應的一些常用案例包括下列項目：
+ 變更狀態以設定 HTTP 200 狀態碼並建立靜態本文內容，以在原始伺服器傳回錯誤狀態碼 (4xx 和 5xx) 時，傳回給檢視器。如需程式碼範例，請參閱 [範例：使用原始伺服器請求觸發以更新錯誤狀態碼為 200](lambda-examples.md#lambda-examples-custom-error-static-body)。
+ 變更狀態來設定 HTTP 301 或 HTTP 302 狀態碼，以在原始伺服器會傳回錯誤狀態碼 (4xx 和 5xx) 時，將使用者重新導向到另一個網站。如需程式碼範例，請參閱 [範例：使用原始伺服器請求觸發以更新錯誤狀態碼為 302](lambda-examples.md#lambda-examples-custom-error-new-site)。

**注意**  
函數必須傳回介於 `200` (含) 和 `599` (含) 之間的狀態值，否則 CloudFront 會將錯誤傳回至檢視器。

您也可以在檢視器與原始伺服器請求事件中取代 HTTP 回應。如需詳細資訊，請參閱[在請求觸發條件中產生 HTTP 回應](#lambda-generating-http-responses-in-requests)。

當您使用 HTTP 回應時，Lambda@Edge 不會公開原始伺服器傳回至原始伺服器回應觸發條件的本文。您可以藉由設定為所需的值來產生靜態內容本文，或藉由設定空值來移除函數內的本文。如果您不更新函數的本文欄位，原始伺服器回傳的原始主體會傳回給檢視器。

## 選擇包含本文選項來存取請求本文
<a name="lambda-include-body-access"></a>

您可以選擇讓 Lambda@Edge 在可寫入的 HTTP 方法 (POST、PUT 和 DELETE 等) 中公開請求的內文，如此您就能在 Lambda 函數中存取該內文。您可以選擇唯讀存取或指定您將替換內文。

若要啟用此選項，請在您為函數建立 CloudFront 觸發條件時 (針對檢視器請求或原始伺服器請求的事件)，選擇 **Include Body (包含內文)**。如需詳細資訊，請參閱 [為 Lambda@Edge 函數新增觸發條件](lambda-edge-add-triggers.md)；或者，若要進一步了解使用**包含內文**與您的函數，請參閱 [Lambda@Edge 事件結構說明頁面](lambda-event-structure.md)。

您可能會想使用此功能的情境，包括下列的案例：
+ 處理 Web 表單，例如「聯絡我們」表單，而不將客戶輸入的資料傳回給原始伺服器。
+ 收集由檢視器的瀏覽器所傳送的網站信標資料，並且在邊緣處理這些資料。

如需程式碼範例，請參閱 [Lambda@Edge 範例函數](lambda-examples.md)。

**注意**  
如果請求主體的資料大小過大，Lambda@Edge 會將主體截斷。如需大小上限和截斷的詳細資訊，請參閱[使用包含內文選項時的要求內文限制](lambda-at-edge-function-restrictions.md#lambda-at-edge-restrictions-request-body)。

# Lambda@Edge 範例函數
<a name="lambda-examples"></a>

請參閱下列透過 Amazon CloudFront 使用 Lambda 函數的範例。

**注意**  
如果您為 Lambda@Edge 函數選擇執行時期 Node.js 18 或更新版本，系統會自動為您建立 `index.mjs` 檔案。若要使用下列程式碼範例，請將 `index.mjs` 檔案重新命名為 `index.js`。

**Topics**
+ [

## 一般範例
](#lambda-examples-general-examples)
+ [

## 產生回應：範例
](#lambda-examples-generated-response-examples)
+ [

## 查詢字串：範例
](#lambda-examples-query-string-examples)
+ [

## 根據國家/地區或裝置類型標頭個人化 - 範例
](#lambda-examples-redirecting-examples)
+ [

## 以內容為基礎的動態原始伺服器選擇 - 範例
](#lambda-examples-content-based-routing-examples)
+ [

## 更新錯誤狀態：範例
](#lambda-examples-update-error-status-examples)
+ [

## 存取請求本文：範例
](#lambda-examples-access-request-body-examples)

## 一般範例
<a name="lambda-examples-general-examples"></a>

下列範例顯示在 CloudFront 中使用 Lambda@Edge 的常見方法。

**Topics**
+ [

### 範例：A/B 測試
](#lambda-examples-a-b-testing)
+ [

### 範例：覆寫回應標頭
](#lambda-examples-overriding-response-header)

### 範例：A/B 測試
<a name="lambda-examples-a-b-testing"></a>

您可以使用下列範例測試兩種不同版本的影像，而不需要建立重新導向或變更 URL。此範例會讀取檢視器請求中的 Cookie，並據此修改請求 URL。如果檢視器未傳送具有其中一個預期值的 Cookie，此範例會將檢視器隨機指派給其中一個 URL。

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    if (request.uri !== '/experiment-pixel.jpg') {
        // do not process if this is not an A-B test request
        callback(null, request);
        return;
    }

    const cookieExperimentA = 'X-Experiment-Name=A';
    const cookieExperimentB = 'X-Experiment-Name=B';
    const pathExperimentA = '/experiment-group/control-pixel.jpg';
    const pathExperimentB = '/experiment-group/treatment-pixel.jpg';

    /*
     * Lambda at the Edge headers are array objects.
     *
     * Client may send multiple Cookie headers, i.e.:
     * > GET /viewerRes/test HTTP/1.1
     * > User-Agent: curl/7.18.1 (x86_64-unknown-linux-gnu) libcurl/7.18.1 OpenSSL/1.0.1u zlib/1.2.3
     * > Cookie: First=1; Second=2
     * > Cookie: ClientCode=abc
     * > Host: example.com
     *
     * You can access the first Cookie header at headers["cookie"][0].value
     * and the second at headers["cookie"][1].value.
     *
     * Header values are not parsed. In the example above,
     * headers["cookie"][0].value is equal to "First=1; Second=2"
     */
    let experimentUri;
    if (headers.cookie) {
        for (let i = 0; i < headers.cookie.length; i++) {
            if (headers.cookie[i].value.indexOf(cookieExperimentA) >= 0) {
                console.log('Experiment A cookie found');
                experimentUri = pathExperimentA;
                break;
            } else if (headers.cookie[i].value.indexOf(cookieExperimentB) >= 0) {
                console.log('Experiment B cookie found');
                experimentUri = pathExperimentB;
                break;
            }
        }
    }

    if (!experimentUri) {
        console.log('Experiment cookie has not been found. Throwing dice...');
        if (Math.random() < 0.75) {
            experimentUri = pathExperimentA;
        } else {
            experimentUri = pathExperimentB;
        }
    }

    request.uri = experimentUri;
    console.log(`Request uri set to "${request.uri}"`);
    callback(null, request);
};
```

------
#### [ Python ]

```
import json
import random

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    if request['uri'] != '/experiment-pixel.jpg':
        # Not an A/B Test
        return request

    cookieExperimentA, cookieExperimentB = 'X-Experiment-Name=A', 'X-Experiment-Name=B'
    pathExperimentA, pathExperimentB = '/experiment-group/control-pixel.jpg', '/experiment-group/treatment-pixel.jpg'

    '''
    Lambda at the Edge headers are array objects.

    Client may send multiple cookie headers. For example:
    > GET /viewerRes/test HTTP/1.1
    > User-Agent: curl/7.18.1 (x86_64-unknown-linux-gnu) libcurl/7.18.1 OpenSSL/1.0.1u zlib/1.2.3
    > Cookie: First=1; Second=2
    > Cookie: ClientCode=abc
    > Host: example.com

    You can access the first Cookie header at headers["cookie"][0].value
    and the second at headers["cookie"][1].value.

    Header values are not parsed. In the example above,
    headers["cookie"][0].value is equal to "First=1; Second=2"
    '''

    experimentUri = ""

    for cookie in headers.get('cookie', []):
        if cookieExperimentA in cookie['value']:
            print("Experiment A cookie found")
            experimentUri = pathExperimentA
            break
        elif cookieExperimentB in cookie['value']:
            print("Experiment B cookie found")
            experimentUri = pathExperimentB
            break

    if not experimentUri:
        print("Experiment cookie has not been found. Throwing dice...")
        if random.random() < 0.75:
            experimentUri = pathExperimentA
        else:
            experimentUri = pathExperimentB

    request['uri'] = experimentUri
    print(f"Request uri set to {experimentUri}")
    return request
```

------

### 範例：覆寫回應標頭
<a name="lambda-examples-overriding-response-header"></a>

以下範例說明如何根據另一個標頭的值變更回應標頭的值。

------
#### [ Node.js ]

```
export const handler = async (event) => {
    const response = event.Records[0].cf.response;
    const headers = response.headers;

    const headerNameSrc = 'X-Amz-Meta-Last-Modified';
    const headerNameDst = 'Last-Modified';

    if (headers[headerNameSrc.toLowerCase()]) {
        headers[headerNameDst.toLowerCase()] = [{
            key: headerNameDst,
            value: headers[headerNameSrc.toLowerCase()][0].value,
        }];
        console.log(`Response header "${headerNameDst}" was set to ` +
                    `"${headers[headerNameDst.toLowerCase()][0].value}"`);
    }

    return response;
};
```

------
#### [ Python ]

```
import json 

def lambda_handler(event, context):
    response = event['Records'][0]['cf']['response']
    headers = response['headers']
    
    header_name_src = 'X-Amz-Meta-Last-Modified'
    header_name_dst = 'Last-Modified'
    
    if headers.get(header_name_src.lower()):
        headers[header_name_dst.lower()] = [{
            'key': header_name_dst,
            'value': headers[header_name_src.lower()][0]['value']
        }]
        print(f'Response header "{header_name_dst}" was set to '
              f'"{headers[header_name_dst.lower()][0]["value"]}"')
    
    return response
```

------

## 產生回應：範例
<a name="lambda-examples-generated-response-examples"></a>

以下範例示範如何使用 Lambda@Edge 來產生回應。

**Topics**
+ [

### 範例：提供靜態內容 (產生回應)
](#lambda-examples-static-web-server)
+ [

### 範例：產生 HTTP 重新導向 (產生回應)
](#lambda-examples-http-redirect)

### 範例：提供靜態內容 (產生回應)
<a name="lambda-examples-static-web-server"></a>

以下範例說明如何使用 Lambda 函數提供靜態網站內容，其可減少原始伺服器的負載，並降低整體延遲。

**注意**  
您可以針對檢視器要求及原始伺服器請求事件產生 HTTP 回應。如需詳細資訊，請參閱[在請求觸發條件中產生 HTTP 回應](lambda-generating-http-responses.md#lambda-generating-http-responses-in-requests)。  
您也可以在原始伺服器回應請求事件中取代或移除 HTTP 回應的主體。如需詳細資訊，請參閱[在原始伺服器回應觸發條件中更新 HTTP 回應](lambda-generating-http-responses.md#lambda-updating-http-responses)。

------
#### [ Node.js ]

```
'use strict';

const content = `
<\!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Simple Lambda@Edge Static Content Response</title>
  </head>
  <body>
    <p>Hello from Lambda@Edge!</p>
  </body>
</html>
`;

exports.handler = (event, context, callback) => {
    /*
     * Generate HTTP OK response using 200 status code with HTML body.
     */
    const response = {
        status: '200',
        statusDescription: 'OK',
        headers: {
            'cache-control': [{
                key: 'Cache-Control',
                value: 'max-age=100'
            }],
            'content-type': [{
                key: 'Content-Type',
                value: 'text/html'
            }]
        },
        body: content,
    };
    callback(null, response);
};
```

------
#### [ Python ]

```
import json

CONTENT = """
<\!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Simple Lambda@Edge Static Content Response</title>
</head>
<body>
    <p>Hello from Lambda@Edge!</p>
</body>
</html>
"""

def lambda_handler(event, context):
    # Generate HTTP OK response using 200 status code with HTML body.
    response = {
        'status': '200',
        'statusDescription': 'OK',
        'headers': {
            'cache-control': [
                {
                    'key': 'Cache-Control',
                    'value': 'max-age=100'
                }
            ],
            "content-type": [
                {
                    'key': 'Content-Type',
                    'value': 'text/html'
                }
            ]
        },
        'body': CONTENT
    }
    return response
```

------

### 範例：產生 HTTP 重新導向 (產生回應)
<a name="lambda-examples-http-redirect"></a>

以下範例說明如何產生 HTTP 重新導向。

**注意**  
您可以針對檢視器要求及原始伺服器請求事件產生 HTTP 回應。如需詳細資訊，請參閱[在請求觸發條件中產生 HTTP 回應](lambda-generating-http-responses.md#lambda-generating-http-responses-in-requests)。

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    /*
     * Generate HTTP redirect response with 302 status code and Location header.
     */
    const response = {
        status: '302',
        statusDescription: 'Found',
        headers: {
            location: [{
                key: 'Location',
                value: 'https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html',
            }],
        },
    };
    callback(null, response);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):

    # Generate HTTP redirect response with 302 status code and Location header.

    response = {
        'status': '302',
        'statusDescription': 'Found',
        'headers': {
            'location': [{
                'key': 'Location',
                'value': 'https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html'
            }]
        }
    }

    return response
```

------

## 查詢字串：範例
<a name="lambda-examples-query-string-examples"></a>

以下範例示範以查詢字串使用 Lambda@Edge 的方式。

**Topics**
+ [

### 範例：根據查詢字串參數新增標頭
](#lambda-examples-header-based-on-query-string)
+ [

### 範例：標準化查詢字串參數，以提升快取點擊率
](#lambda-examples-normalize-query-string-parameters)
+ [

### 範例：重新導向未驗證使用者到登入頁面
](#lambda-examples-redirect-to-signin-page)

### 範例：根據查詢字串參數新增標頭
<a name="lambda-examples-header-based-on-query-string"></a>

以下範例說明如何取得查詢字串參數的鍵值對，然後根據這些值新增標頭。

------
#### [ Node.js ]

```
'use strict';

const querystring = require('querystring');
exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    
    /* When a request contains a query string key-value pair but the origin server
     * expects the value in a header, you can use this Lambda function to
     * convert the key-value pair to a header. Here's what the function does:
     * 1. Parses the query string and gets the key-value pair.
     * 2. Adds a header to the request using the key-value pair that the function got in step 1.
     */

    /* Parse request querystring to get javascript object */
    const params = querystring.parse(request.querystring);

    /* Move auth param from querystring to headers */
    const headerName = 'Auth-Header';
    request.headers[headerName.toLowerCase()] = [{ key: headerName, value: params.auth }];
    delete params.auth;

    /* Update request querystring */
    request.querystring = querystring.stringify(params);

    callback(null, request);
};
```

------
#### [ Python ]

```
from urllib.parse import parse_qs, urlencode

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    '''
    When a request contains a query string key-value pair but the origin server
    expects the value in a header, you can use this Lambda function to
    convert the key-value pair to a header. Here's what the function does:
        1. Parses the query string and gets the key-value pair.
        2. Adds a header to the request using the key-value pair that the function got in step 1.
    '''

    # Parse request querystring to get dictionary/json
    params = {k : v[0] for k, v in parse_qs(request['querystring']).items()}

    # Move auth param from querystring to headers
    headerName = 'Auth-Header'
    request['headers'][headerName.lower()] = [{'key': headerName, 'value': params['auth']}]
    del params['auth']

    # Update request querystring
    request['querystring'] = urlencode(params)

    return request
```

------

### 範例：標準化查詢字串參數，以提升快取點擊率
<a name="lambda-examples-normalize-query-string-parameters"></a>

以下範例說明如何在 CloudFront 轉送請求至您的原始伺服器之前，做以下變更至查詢字串以提高您的快取點擊率：
+ 依字母排序鍵值組的參數名稱。
+ 將鍵值組變更為小寫。

如需詳細資訊，請參閱[根據查詢字串參數快取內容](QueryStringParameters.md)。

------
#### [ Node.js ]

```
'use strict';

const querystring = require('querystring');

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    /* When you configure a distribution to forward query strings to the origin and
     * to cache based on an allowlist of query string parameters, we recommend
     * the following to improve the cache-hit ratio:
     * - Always list parameters in the same order.
     * - Use the same case for parameter names and values.
     *
     * This function normalizes query strings so that parameter names and values
     * are lowercase and parameter names are in alphabetical order.
     *
     * For more information, see:
     * https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/QueryStringParameters.html
     */

    console.log('Query String: ', request.querystring);

    /* Parse request query string to get javascript object */
    const params = querystring.parse(request.querystring.toLowerCase());
    const sortedParams = {};

    /* Sort param keys */
    Object.keys(params).sort().forEach(key => {
        sortedParams[key] = params[key];
    });

    /* Update request querystring with normalized  */
    request.querystring = querystring.stringify(sortedParams);

    callback(null, request);
};
```

------
#### [ Python ]

```
from urllib.parse import parse_qs, urlencode

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    '''
    When you configure a distribution to forward query strings to the origin and
    to cache based on an allowlist of query string parameters, we recommend
    the following to improve the cache-hit ratio:
    Always list parameters in the same order.
    - Use the same case for parameter names and values.

    This function normalizes query strings so that parameter names and values
    are lowercase and parameter names are in alphabetical order.

    For more information, see:
    https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/QueryStringParameters.html
    '''
    print("Query string: ", request["querystring"])

    # Parse request query string to get js object
    params = {k : v[0] for k, v in parse_qs(request['querystring'].lower()).items()}

    # Sort param keys
    sortedParams = sorted(params.items(), key=lambda x: x[0])

    # Update request querystring with normalized
    request['querystring'] = urlencode(sortedParams)
    
    return request
```

------

### 範例：重新導向未驗證使用者到登入頁面
<a name="lambda-examples-redirect-to-signin-page"></a>

以下範例說明，如果尚未輸入他們的登入資料，如何將使用者重新導向至登入頁面。

------
#### [ Node.js ]

```
'use strict';

function parseCookies(headers) {
    const parsedCookie = {};
    if (headers.cookie) {
        headers.cookie[0].value.split(';').forEach((cookie) => {
            if (cookie) {
                const parts = cookie.split('=');
                parsedCookie[parts[0].trim()] = parts[1].trim();
            }
        });
    }
    return parsedCookie;
}

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    /* Check for session-id in request cookie in viewer-request event,
     * if session-id is absent, redirect the user to sign in page with original
     * request sent as redirect_url in query params.
     */

    /* Check for session-id in cookie, if present then proceed with request */
    const parsedCookies = parseCookies(headers);
    if (parsedCookies && parsedCookies['session-id']) {
        callback(null, request);
        return;
    }

    /* URI encode the original request to be sent as redirect_url in query params */
    const encodedRedirectUrl = encodeURIComponent(`https://${headers.host[0].value}${request.uri}?${request.querystring}`);
    const response = {
        status: '302',
        statusDescription: 'Found',
        headers: {
            location: [{
                key: 'Location',
                value: `https://www.example.com/signin?redirect_url=${encodedRedirectUrl}`,
            }],
        },
    };
    callback(null, response);
};
```

------
#### [ Python ]

```
import urllib

def parseCookies(headers):
    parsedCookie = {}
    if headers.get('cookie'):
        for cookie in headers['cookie'][0]['value'].split(';'):
            if cookie:
                parts = cookie.split('=')
                parsedCookie[parts[0].strip()] = parts[1].strip()
    return parsedCookie

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    '''
    Check for session-id in request cookie in viewer-request event,
    if session-id is absent, redirect the user to sign in page with original
    request sent as redirect_url in query params.
    '''

    # Check for session-id in cookie, if present, then proceed with request
    parsedCookies = parseCookies(headers)

    if parsedCookies and parsedCookies['session-id']:
        return request

    # URI encode the original request to be sent as redirect_url in query params
    redirectUrl = "https://%s%s?%s" % (headers['host'][0]['value'], request['uri'], request['querystring'])
    encodedRedirectUrl = urllib.parse.quote_plus(redirectUrl.encode('utf-8'))

    response = {
        'status': '302',
        'statusDescription': 'Found',
        'headers': {
            'location': [{
                'key': 'Location',
                'value': 'https://www.example.com/signin?redirect_url=%s' % encodedRedirectUrl
            }]
        }
    }
    return response
```

------

## 根據國家/地區或裝置類型標頭個人化 - 範例
<a name="lambda-examples-redirecting-examples"></a>

以下範例說明如何使用 Lambda@Edge 來根據位置或檢視器使用的裝置類型自訂行為。

**Topics**
+ [

### 範例：將檢視器請求重新導向到國家/地區特定的 URL
](#lambda-examples-redirect-based-on-country)
+ [

### 範例：根據裝置提供不同的物件版本
](#lambda-examples-vary-on-device-type)

### 範例：將檢視器請求重新導向到國家/地區特定的 URL
<a name="lambda-examples-redirect-based-on-country"></a>

以下範例說明如何產生含國家/地區特定 URL 的 HTTP 重新導向回應，並將回應傳回至檢視器。當您希望提供特定國家的回應時，此方法很有用。例如：
+ 如果您有國家/地區特定的子網域 (例如 us.example.com 和 tw.example.com)，當檢視器請求 example.com 時，您可以產生一個重新導向回應。
+ 如果您在串流視訊，但沒有在特定國家/地區串流此內容的權利，您可以在該國家/地區將使用者重新導向到說明他們為何無法檢視影片的頁面。

注意下列事項：
+ 您必須根據 `CloudFront-Viewer-Country` 標頭設定您的分佈為快取。如需詳細資訊，請參閱[根據選取請求標頭的快取](DownloadDistValuesCacheBehavior.md#DownloadDistValuesForwardHeaders)。
+ 在檢視器請求事件後，CloudFront 新增 `CloudFront-Viewer-Country` 標頭。要使用此範例，您必須建立原始伺服器請求事件的觸發。

------
#### [ Node.js ]

```
'use strict';

/* This is an origin request function */
exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    /*
     * Based on the value of the CloudFront-Viewer-Country header, generate an
     * HTTP status code 302 (Redirect) response, and return a country-specific
     * URL in the Location header.
     * NOTE: 1. You must configure your distribution to cache based on the
     *          CloudFront-Viewer-Country header. For more information, see
     *          https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
     *       2. CloudFront adds the CloudFront-Viewer-Country header after the viewer
     *          request event. To use this example, you must create a trigger for the
     *          origin request event.
     */

    let url = 'https://example.com/';
    if (headers['cloudfront-viewer-country']) {
        const countryCode = headers['cloudfront-viewer-country'][0].value;
        if (countryCode === 'TW') {
            url = 'https://tw.example.com/';
        } else if (countryCode === 'US') {
            url = 'https://us.example.com/';
        }
    }

    const response = {
        status: '302',
        statusDescription: 'Found',
        headers: {
            location: [{
                key: 'Location',
                value: url,
            }],
        },
    };
    callback(null, response);
};
```

------
#### [ Python ]

```
# This is an origin request function

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    '''
    Based on the value of the CloudFront-Viewer-Country header, generate an
    HTTP status code 302 (Redirect) response, and return a country-specific
    URL in the Location header.
    NOTE: 1. You must configure your distribution to cache based on the
            CloudFront-Viewer-Country header. For more information, see
            https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
          2. CloudFront adds the CloudFront-Viewer-Country header after the viewer
            request event. To use this example, you must create a trigger for the
            origin request event.
    '''

    url = 'https://example.com/'
    viewerCountry = headers.get('cloudfront-viewer-country')
    if viewerCountry:
        countryCode = viewerCountry[0]['value']
        if countryCode == 'TW':
            url = 'https://tw.example.com/'
        elif countryCode == 'US':
            url = 'https://us.example.com/'

    response = {
        'status': '302',
        'statusDescription': 'Found',
        'headers': {
            'location': [{
                'key': 'Location',
                'value': url
            }]
        }
    }

    return response
```

------

### 範例：根據裝置提供不同的物件版本
<a name="lambda-examples-vary-on-device-type"></a>

以下範例說明如何根據使用者使用的裝置類型，提供不同的物件版本，例如行動裝置或平板電腦。注意下列事項：
+ 您必須根據 `CloudFront-Is-*-Viewer` 標頭設定您的分佈為快取。如需詳細資訊，請參閱[根據選取請求標頭的快取](DownloadDistValuesCacheBehavior.md#DownloadDistValuesForwardHeaders)。
+ 在檢視器請求事件後，CloudFront 新增 `CloudFront-Is-*-Viewer` 標頭。要使用此範例，您必須建立原始伺服器請求事件的觸發。

------
#### [ Node.js ]

```
'use strict';

/* This is an origin request function */
exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    /*
     * Serve different versions of an object based on the device type.
     * NOTE: 1. You must configure your distribution to cache based on the
     *          CloudFront-Is-*-Viewer headers. For more information, see
     *          the following documentation:
     *          https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
     *          https://docs.aws.amazon.com/console/cloudfront/cache-on-device-type
     *       2. CloudFront adds the CloudFront-Is-*-Viewer headers after the viewer
     *          request event. To use this example, you must create a trigger for the
     *          origin request event.
     */

    const desktopPath = '/desktop';
    const mobilePath = '/mobile';
    const tabletPath = '/tablet';
    const smarttvPath = '/smarttv';

    if (headers['cloudfront-is-desktop-viewer']
        && headers['cloudfront-is-desktop-viewer'][0].value === 'true') {
        request.uri = desktopPath + request.uri;
    } else if (headers['cloudfront-is-mobile-viewer']
               && headers['cloudfront-is-mobile-viewer'][0].value === 'true') {
        request.uri = mobilePath + request.uri;
    } else if (headers['cloudfront-is-tablet-viewer']
               && headers['cloudfront-is-tablet-viewer'][0].value === 'true') {
        request.uri = tabletPath + request.uri;
    } else if (headers['cloudfront-is-smarttv-viewer']
               && headers['cloudfront-is-smarttv-viewer'][0].value === 'true') {
        request.uri = smarttvPath + request.uri;
    }
    console.log(`Request uri set to "${request.uri}"`);

    callback(null, request);
};
```

------
#### [ Python ]

```
# This is an origin request function
def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    '''
    Serve different versions of an object based on the device type.
    NOTE: 1. You must configure your distribution to cache based on the
            CloudFront-Is-*-Viewer headers. For more information, see
            the following documentation:
            https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
            https://docs.aws.amazon.com/console/cloudfront/cache-on-device-type
          2. CloudFront adds the CloudFront-Is-*-Viewer headers after the viewer
            request event. To use this example, you must create a trigger for the
            origin request event.
    '''

    desktopPath = '/desktop';
    mobilePath = '/mobile';
    tabletPath = '/tablet';
    smarttvPath = '/smarttv';

    if 'cloudfront-is-desktop-viewer' in headers and headers['cloudfront-is-desktop-viewer'][0]['value'] == 'true':
        request['uri'] = desktopPath + request['uri']
    elif 'cloudfront-is-mobile-viewer' in headers and headers['cloudfront-is-mobile-viewer'][0]['value'] == 'true':
        request['uri'] = mobilePath + request['uri']
    elif 'cloudfront-is-tablet-viewer' in headers and headers['cloudfront-is-tablet-viewer'][0]['value'] == 'true':
        request['uri'] = tabletPath + request['uri']
    elif 'cloudfront-is-smarttv-viewer' in headers and headers['cloudfront-is-smarttv-viewer'][0]['value'] == 'true':
        request['uri'] = smarttvPath + request['uri']

    print("Request uri set to %s" % request['uri'])

    return request
```

------

## 以內容為基礎的動態原始伺服器選擇 - 範例
<a name="lambda-examples-content-based-routing-examples"></a>

以下範例示範如何使用 Lambda@Edge 來根據請求中的資訊路由到不同的原始伺服器。

**Topics**
+ [

### 範例：使用原始伺服器請求觸發條件，以從自訂原始伺服器變更到 Amazon S3 原始伺服器
](#lambda-examples-content-based-S3-origin-based-on-query)
+ [

### 範例：使用原始伺服器請求觸發條件，以變更 Amazon S3 原始伺服器區域
](#lambda-examples-content-based-S3-origin-request-trigger)
+ [

### 範例：使用原始伺服器請求觸發條件，變更 Amazon S3 原始伺服器為自訂原始伺服器
](#lambda-examples-content-based-custom-origin-request-trigger)
+ [

### 範例：使用原始伺服器請求觸發條件，將流量自一個 Amazon S3 儲存貯體逐漸傳輸到另一個。
](#lambda-examples-content-based-gradual-traffic-transfer)
+ [

### 範例：使用原始伺服器請求觸發，根據國家/地區標頭變更原始網域名稱
](#lambda-examples-content-based-geo-header)

### 範例：使用原始伺服器請求觸發條件，以從自訂原始伺服器變更到 Amazon S3 原始伺服器
<a name="lambda-examples-content-based-S3-origin-based-on-query"></a>

此函數示範如何使用 origin-request 觸發條件，根據請求屬性從自訂原始伺服器變更至內容被擷取的 Amazon S3 原始伺服器。

------
#### [ Node.js ]

```
'use strict';

 const querystring = require('querystring');
 
 exports.handler = (event, context, callback) => {
     const request = event.Records[0].cf.request;
 
     /**
      * Reads query string to check if S3 origin should be used, and
      * if true, sets S3 origin properties.
      */
 
     const params = querystring.parse(request.querystring);
 
     if (params['useS3Origin']) {
         if (params['useS3Origin'] === 'true') {
             const s3DomainName = 'amzn-s3-demo-bucket.s3.amazonaws.com';
 
             /* Set S3 origin fields */
             request.origin = {
                 s3: {
                     domainName: s3DomainName,
                     region: '',
                     authMethod: 'origin-access-identity',
                     path: '',
                     customHeaders: {}
                 }
             };
             request.headers['host'] = [{ key: 'host', value: s3DomainName}];
         }
     }
     
    callback(null, request);
};
```

------
#### [ Python ]

```
from urllib.parse import parse_qs

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    '''
    Reads query string to check if S3 origin should be used, and
    if true, sets S3 origin properties
    '''
    params = {k: v[0] for k, v in parse_qs(request['querystring']).items()}
    if params.get('useS3Origin') == 'true':
        s3DomainName = 'amzn-s3-demo-bucket.s3.amazonaws.com'

        # Set S3 origin fields
        request['origin'] = {
            's3': {
                'domainName': s3DomainName,
                'region': '',
                'authMethod': 'origin-access-identity',
                'path': '',
                'customHeaders': {}
            }
        }
        request['headers']['host'] = [{'key': 'host', 'value': s3DomainName}]
    return request
```

------

### 範例：使用原始伺服器請求觸發條件，以變更 Amazon S3 原始伺服器區域
<a name="lambda-examples-content-based-S3-origin-request-trigger"></a>

此函數示範如何根據請求屬性，使用 origin-request 觸發條件來變更內容被擷取的 Amazon S3 原始伺服器。

在這個範例中，我們使用 `CloudFront-Viewer-Country` 標頭的值來將 S3 儲存貯體的網域名稱更新為較靠近檢視器的區域內的儲存貯體。這在數種方式中非常受用：
+ 當指定的區域更靠近檢視器的國家/地區時，能減少延遲。
+ 藉由確保該資料與請求來自位於相同國家/地區的原始伺服器，提供資料主權服務。

要使用此範例，須執行下列項目：
+ 您必須根據 `CloudFront-Viewer-Country` 標頭設定您的分佈為快取。如需詳細資訊，請參閱[根據選取請求標頭的快取](DownloadDistValuesCacheBehavior.md#DownloadDistValuesForwardHeaders)。
+ 在原始伺服器請求事件中建立此函數的觸發。CloudFront 將在檢視者請求事件後新增 `CloudFront-Viewer-Country` 標頭，所以若要使用此範例，必須確認函數會執行原始請求。

**注意**  
下列範例程式碼針對您用於原始伺服器的所有 S3 儲存貯體使用相同的原始存取身分 (OAI)。如需更多詳細資訊，請參閱[原始存取身分](private-content-restricting-access-to-s3.md#private-content-restricting-access-to-s3-oai)。

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;

    /**
     * This blueprint demonstrates how an origin-request trigger can be used to
     * change the origin from which the content is fetched, based on request properties.
     * In this example, we use the value of the CloudFront-Viewer-Country header
     * to update the S3 bucket domain name to a bucket in a Region that is closer to
     * the viewer.
     * 
     * This can be useful in several ways:
     *      1) Reduces latencies when the Region specified is nearer to the viewer's
     *         country.
     *      2) Provides data sovereignty by making sure that data is served from an
     *         origin that's in the same country that the request came from.
     * 
     * NOTE: 1. You must configure your distribution to cache based on the
     *          CloudFront-Viewer-Country header. For more information, see
     *          https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
     *       2. CloudFront adds the CloudFront-Viewer-Country header after the viewer
     *          request event. To use this example, you must create a trigger for the
     *          origin request event.
     */

    const countryToRegion = {
        'DE': 'eu-central-1',
        'IE': 'eu-west-1',
        'GB': 'eu-west-2',
        'FR': 'eu-west-3',
        'JP': 'ap-northeast-1',
        'IN': 'ap-south-1'
    };

    if (request.headers['cloudfront-viewer-country']) {
        const countryCode = request.headers['cloudfront-viewer-country'][0].value;
        const region = countryToRegion[countryCode];
        
        /**
         * If the viewer's country is not in the list you specify, the request
         * goes to the default S3 bucket you've configured.
         */  
        if (region) {
            /**
             * If you've set up OAI, the bucket policy in the destination bucket
             * should allow the OAI GetObject operation, as configured by default
             * for an S3 origin with OAI. Another requirement with OAI is to provide
             * the Region so it can be used for the SIGV4 signature. Otherwise, the
             * Region is not required.
             */
            request.origin.s3.region = region;
            const domainName = `amzn-s3-demo-bucket-in-${region}.s3.${region}.amazonaws.com`;
            request.origin.s3.domainName = domainName;
            request.headers['host'] = [{ key: 'host', value: domainName }];
        }
    }

    callback(null, request);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    '''
    This blueprint demonstrates how an origin-request trigger can be used to
    change the origin from which the content is fetched, based on request properties.
    In this example, we use the value of the CloudFront-Viewer-Country header
    to update the S3 bucket domain name to a bucket in a Region that is closer to
    the viewer.
    
    This can be useful in several ways:
        1) Reduces latencies when the Region specified is nearer to the viewer's
            country.
        2) Provides data sovereignty by making sure that data is served from an
            origin that's in the same country that the request came from.
    
    NOTE: 1. You must configure your distribution to cache based on the
            CloudFront-Viewer-Country header. For more information, see
            https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
          2. CloudFront adds the CloudFront-Viewer-Country header after the viewer
            request event. To use this example, you must create a trigger for the
            origin request event.
    '''

    countryToRegion = {
        'DE': 'eu-central-1',
        'IE': 'eu-west-1',
        'GB': 'eu-west-2',
        'FR': 'eu-west-3',
        'JP': 'ap-northeast-1',
        'IN': 'ap-south-1'
    }

    viewerCountry = request['headers'].get('cloudfront-viewer-country')
    if viewerCountry:
        countryCode = viewerCountry[0]['value']
        region = countryToRegion.get(countryCode)

        # If the viewer's country in not in the list you specify, the request
        # goes to the default S3 bucket you've configured
        if region:
            '''
            If you've set up OAI, the bucket policy in the destination bucket
            should allow the OAI GetObject operation, as configured by default
            for an S3 origin with OAI. Another requirement with OAI is to provide
            the Region so it can be used for the SIGV4 signature. Otherwise, the
            Region is not required.
            '''
            request['origin']['s3']['region'] = region
            domainName = 'amzn-s3-demo-bucket-in-{0}.s3.{0}.amazonaws.com'.format(region)
            request['origin']['s3']['domainName'] = domainName
            request['headers']['host'] = [{'key': 'host', 'value': domainName}]

    return request
```

------

### 範例：使用原始伺服器請求觸發條件，變更 Amazon S3 原始伺服器為自訂原始伺服器
<a name="lambda-examples-content-based-custom-origin-request-trigger"></a>

此函數示範如何根據請求屬性，使用原始伺服器請求觸發來變更內容被擷取的自訂原始伺服器。

------
#### [ Node.js ]

```
'use strict';

const querystring = require('querystring');
 
 exports.handler = (event, context, callback) => {
     const request = event.Records[0].cf.request;
 
     /**
      * Reads query string to check if custom origin should be used, and
      * if true, sets custom origin properties.
      */
 
     const params = querystring.parse(request.querystring);
 
     if (params['useCustomOrigin']) {
         if (params['useCustomOrigin'] === 'true') {
 
             /* Set custom origin fields*/
             request.origin = {
                 custom: {
                     domainName: 'www.example.com',
                     port: 443,
                     protocol: 'https',
                     path: '',
                     sslProtocols: ['TLSv1', 'TLSv1.1'],
                     readTimeout: 5,
                     keepaliveTimeout: 5,
                     customHeaders: {}
                 }
             };
             request.headers['host'] = [{ key: 'host', value: 'www.example.com'}];
         }
     }
    callback(null, request);
};
```

------
#### [ Python ]

```
from urllib.parse import parse_qs

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    # Reads query string to check if custom origin should be used, and
    # if true, sets custom origin properties

    params = {k: v[0] for k, v in parse_qs(request['querystring']).items()}

    if params.get('useCustomOrigin') == 'true':
            # Set custom origin fields
            request['origin'] = {
                'custom': {
                    'domainName': 'www.example.com',
                    'port': 443,
                    'protocol': 'https',
                    'path': '',
                    'sslProtocols': ['TLSv1', 'TLSv1.1'],
                    'readTimeout': 5,
                    'keepaliveTimeout': 5,
                    'customHeaders': {}
                }
            }
            request['headers']['host'] = [{'key': 'host', 'value': 'www.example.com'}]

    return request
```

------

### 範例：使用原始伺服器請求觸發條件，將流量自一個 Amazon S3 儲存貯體逐漸傳輸到另一個。
<a name="lambda-examples-content-based-gradual-traffic-transfer"></a>

此函數示範如何以可控的方式，從一個 Amazon S3 儲存貯體逐步傳輸流量到另一個儲存貯體。

------
#### [ Node.js ]

```
'use strict';

    function getRandomInt(min, max) {
        /* Random number is inclusive of min and max*/
        return Math.floor(Math.random() * (max - min + 1)) + min;
 }

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const BLUE_TRAFFIC_PERCENTAGE = 80;

    /**
      * This Lambda function demonstrates how to gradually transfer traffic from
      * one S3 bucket to another in a controlled way.
      * We define a variable BLUE_TRAFFIC_PERCENTAGE which can take values from
      * 1 to 100. If the generated randomNumber less than or equal to BLUE_TRAFFIC_PERCENTAGE, traffic
      * is re-directed to blue-bucket. If not, the default bucket that we've configured
      * is used.
      */

    const randomNumber = getRandomInt(1, 100);

if (randomNumber <= BLUE_TRAFFIC_PERCENTAGE) {
         const domainName = 'blue-bucket.s3.amazonaws.com';
         request.origin.s3.domainName = domainName;
         request.headers['host'] = [{ key: 'host', value: domainName}];
     }
    callback(null, request);
};
```

------
#### [ Python ]

```
import math
import random

def getRandomInt(min, max):
    # Random number is inclusive of min and max
    return math.floor(random.random() * (max - min + 1)) + min

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    BLUE_TRAFFIC_PERCENTAGE = 80

    '''
    This Lambda function demonstrates how to gradually transfer traffic from
    one S3 bucket to another in a controlled way.
    We define a variable BLUE_TRAFFIC_PERCENTAGE which can take values from
    1 to 100. If the generated randomNumber less than or equal to BLUE_TRAFFIC_PERCENTAGE, traffic
    is re-directed to blue-bucket. If not, the default bucket that we've configured
    is used.
    '''

    randomNumber = getRandomInt(1, 100)

    if randomNumber <= BLUE_TRAFFIC_PERCENTAGE:
        domainName = 'blue-bucket.s3.amazonaws.com'
        request['origin']['s3']['domainName'] = domainName
        request['headers']['host'] = [{'key': 'host', 'value': domainName}]

    return request
```

------

### 範例：使用原始伺服器請求觸發，根據國家/地區標頭變更原始網域名稱
<a name="lambda-examples-content-based-geo-header"></a>

此函數示範如何根據 `CloudFront-Viewer-Country` 標頭來變更原始伺服器的網域名稱，使內容能從靠近檢視者國家/地區的原始伺服器提供。

為您的分佈實施此功能可以擁有如下所述的優勢：
+ 當指定的區域更靠近檢視器的國家/地區時，可減少延遲
+ 藉由確保該資料與請求來自位於相同國家/地區的原始伺服器來提供資料主權服務

請注意，要啟用此功能，您必須根據 `CloudFront-Viewer-Country` 標頭設定您的分佈為快取。如需詳細資訊，請參閱[根據選取請求標頭的快取](DownloadDistValuesCacheBehavior.md#DownloadDistValuesForwardHeaders)。

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
     const request = event.Records[0].cf.request;
     
  if (request.headers['cloudfront-viewer-country']) {
         const countryCode = request.headers['cloudfront-viewer-country'][0].value;
         if (countryCode === 'GB' || countryCode === 'DE' || countryCode === 'IE' ) {
             const domainName = 'eu.example.com';
             request.origin.custom.domainName = domainName;
             request.headers['host'] = [{key: 'host', value: domainName}];
         } 
     }
     
    callback(null, request);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    viewerCountry = request['headers'].get('cloudfront-viewer-country')
    if viewerCountry:
        countryCode = viewerCountry[0]['value']
        if countryCode == 'GB' or countryCode == 'DE' or countryCode == 'IE':
            domainName = 'eu.example.com'
            request['origin']['custom']['domainName'] = domainName
            request['headers']['host'] = [{'key': 'host', 'value': domainName}]
    return request
```

------

## 更新錯誤狀態：範例
<a name="lambda-examples-update-error-status-examples"></a>

以下範例提供指導，說明如何使用 Lambda@Edge 來變更傳回給使用者的錯誤狀態。

**Topics**
+ [

### 範例：使用原始伺服器請求觸發以更新錯誤狀態碼為 200
](#lambda-examples-custom-error-static-body)
+ [

### 範例：使用原始伺服器請求觸發以更新錯誤狀態碼為 302
](#lambda-examples-custom-error-new-site)

### 範例：使用原始伺服器請求觸發以更新錯誤狀態碼為 200
<a name="lambda-examples-custom-error-static-body"></a>

此函數示範如何更新回應狀態為 200 並產生靜態本文內容，以在以下案例傳回給檢視器：
+ 函數在原始伺服器回應中觸發。
+ 原始伺服器的回應狀態為錯誤狀態碼 (4xx 和 5xx)

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    const response = event.Records[0].cf.response;

    /**
     * This function updates the response status to 200 and generates static
     * body content to return to the viewer in the following scenario:
     * 1. The function is triggered in an origin response
     * 2. The response status from the origin server is an error status code (4xx or 5xx)
     */

    if (response.status >= 400 && response.status <= 599) {
        response.status = 200;
        response.statusDescription = 'OK';
        response.body = 'Body generation example';
    }

    callback(null, response);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):
    response = event['Records'][0]['cf']['response']

    '''
    This function updates the response status to 200 and generates static
    body content to return to the viewer in the following scenario:
    1. The function is triggered in an origin response
    2. The response status from the origin server is an error status code (4xx or 5xx)
    '''

    if int(response['status']) >= 400 and int(response['status']) <= 599:
        response['status'] = 200
        response['statusDescription'] = 'OK'
        response['body'] = 'Body generation example'
    return response
```

------

### 範例：使用原始伺服器請求觸發以更新錯誤狀態碼為 302
<a name="lambda-examples-custom-error-new-site"></a>

此函數示範如何更新 HTTP 狀態碼為 302，以重新導向到另一個由不同原始伺服器設定的路徑 (快取行為)。注意下列事項：
+ 函數在原始伺服器回應中觸發。
+ 原始伺服器的回應狀態為錯誤狀態碼 (4xx 和 5xx)

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    const response = event.Records[0].cf.response;
    const request = event.Records[0].cf.request;

    /**
     * This function updates the HTTP status code in the response to 302, to redirect to another
     * path (cache behavior) that has a different origin configured. Note the following:
     * 1. The function is triggered in an origin response
     * 2. The response status from the origin server is an error status code (4xx or 5xx)
     */

    if (response.status >= 400 && response.status <= 599) {
        const redirect_path = `/plan-b/path?${request.querystring}`;

        response.status = 302;
        response.statusDescription = 'Found';

        /* Drop the body, as it is not required for redirects */
        response.body = '';
        response.headers['location'] = [{ key: 'Location', value: redirect_path }];
    }

    callback(null, response);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):
    response = event['Records'][0]['cf']['response']
    request = event['Records'][0]['cf']['request']

    '''
    This function updates the HTTP status code in the response to 302, to redirect to another
    path (cache behavior) that has a different origin configured. Note the following:
    1. The function is triggered in an origin response
    2. The response status from the origin server is an error status code (4xx or 5xx)
    '''

    if int(response['status']) >= 400 and int(response['status']) <= 599:
        redirect_path = '/plan-b/path?%s' % request['querystring']

        response['status'] = 302
        response['statusDescription'] = 'Found'

        # Drop the body as it is not required for redirects
        response['body'] = ''
        response['headers']['location'] = [{'key': 'Location', 'value': redirect_path}]

    return response
```

------

## 存取請求本文：範例
<a name="lambda-examples-access-request-body-examples"></a>

以下範例說明如何使用 Lambda@Edge 來處理 POST 請求。

**注意**  
若要使用這些範例，您必須在分佈的 Lambda 函數關聯中啟用*包含內文*選項。依預設不會啟用此功能。  
若要在 CloudFront 主控台中啟用此設定，請選取 **Lambda 函數關聯**中**包含內文**的核取方塊。
若要在 CloudFront API 中或使用 啟用此設定 CloudFormation，請在 中將 `IncludeBody` 欄位設定為 `true` `LambdaFunctionAssociation`。

**Topics**
+ [

### 範例：使用請求觸發條件來讀取 HTML 表單
](#lambda-examples-access-request-body-examples-read)
+ [

### 範例：使用請求觸發條件來修改 HTML 表單
](#lambda-examples-access-request-body-examples-replace)

### 範例：使用請求觸發條件來讀取 HTML 表單
<a name="lambda-examples-access-request-body-examples-read"></a>

此函數示範如何處理 HTML 表單 (Web 表單) 所產生 POST 請求的主體，例如「聯絡我們」表單。例如，您可能會有如下的 HTML 表單：

```
<html>
  <form action="https://example.com" method="post">
    Param 1: <input type="text" name="name1"><br>
    Param 2: <input type="text" name="name2"><br>
    input type="submit" value="Submit">
  </form>
</html>
```

至於後面的範例函數，該函數必須在 CloudFront 檢視器請求或原始伺服器請求中觸發。

------
#### [ Node.js ]

```
'use strict';

const querystring = require('querystring');

/**
 * This function demonstrates how you can read the body of a POST request 
 * generated by an HTML form (web form). The function is triggered in a
 * CloudFront viewer request or origin request event type.
 */

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;

    if (request.method === 'POST') {
        /* HTTP body is always passed as base64-encoded string. Decode it. */
        const body = Buffer.from(request.body.data, 'base64').toString();
 
        /* HTML forms send the data in query string format. Parse it. */
        const params = querystring.parse(body);
 
        /* For demonstration purposes, we only log the form fields here.
         * You can put your custom logic here. For example, you can store the 
         * fields in a database, such as Amazon DynamoDB, and generate a response
         * right from your Lambda@Edge function.
         */
        for (let param in params) {
            console.log(`For "${param}" user submitted "${params[param]}".\n`);
        }
    }
    return callback(null, request);
};
```

------
#### [ Python ]

```
import base64
from urllib.parse import parse_qs

'''
Say there is a POST request body generated by an HTML such as:

<html>
<form action="https://example.com" method="post">
    Param 1: <input type="text" name="name1"><br>
    Param 2: <input type="text" name="name2"><br>
    input type="submit" value="Submit">
</form>
</html>

'''

'''
This function demonstrates how you can read the body of a POST request 
generated by an HTML form (web form). The function is triggered in a
CloudFront viewer request or origin request event type.
'''

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    if request['method'] == 'POST':
        # HTTP body is always passed as base64-encoded string. Decode it
        body = base64.b64decode(request['body']['data'])

        # HTML forms send the data in query string format. Parse it
        params = {k: v[0] for k, v in parse_qs(body).items()}

        '''
        For demonstration purposes, we only log the form fields here.
        You can put your custom logic here. For example, you can store the
        fields in a database, such as Amazon DynamoDB, and generate a response
        right from your Lambda@Edge function.
        '''
        for key, value in params.items():
            print("For %s use submitted %s" % (key, value))
            
    return request
```

------

### 範例：使用請求觸發條件來修改 HTML 表單
<a name="lambda-examples-access-request-body-examples-replace"></a>

此函數示範如何修改 HTML 表單 (Web 表單) 所產生 POST 請求的主體。此函數會在 CloudFront 檢視器請求或原始伺服器請求中觸發。

------
#### [ Node.js ]

```
'use strict';
				
const querystring = require('querystring');

exports.handler = (event, context, callback) => {
    var request = event.Records[0].cf.request;
    if (request.method === 'POST') {
        /* Request body is being replaced. To do this, update the following
        /* three fields:
         *    1) body.action to 'replace'
         *    2) body.encoding to the encoding of the new data.
         *
         *       Set to one of the following values:
         *
         *           text - denotes that the generated body is in text format.
         *               Lambda@Edge will propagate this as is.
         *           base64 - denotes that the generated body is base64 encoded.
         *               Lambda@Edge will base64 decode the data before sending
         *               it to the origin.
         *    3) body.data to the new body.
         */
        request.body.action = 'replace';
        request.body.encoding = 'text';
        request.body.data = getUpdatedBody(request);
    }
    callback(null, request);
};

function getUpdatedBody(request) {
    /* HTTP body is always passed as base64-encoded string. Decode it. */
    const body = Buffer.from(request.body.data, 'base64').toString();

    /* HTML forms send data in query string format. Parse it. */
    const params = querystring.parse(body);

    /* For demonstration purposes, we're adding one more param.
     *
     * You can put your custom logic here. For example, you can truncate long
     * bodies from malicious requests.
     */
    params['new-param-name'] = 'new-param-value';
    return querystring.stringify(params);
}
```

------
#### [ Python ]

```
import base64
from urllib.parse import parse_qs, urlencode

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    if request['method'] == 'POST':
        '''
        Request body is being replaced. To do this, update the following
        three fields:
            1) body.action to 'replace'
            2) body.encoding to the encoding of the new data.
        
            Set to one of the following values:
        
                text - denotes that the generated body is in text format.
                    Lambda@Edge will propagate this as is.
                base64 - denotes that the generated body is base64 encoded.
                    Lambda@Edge will base64 decode the data before sending
                    it to the origin.
            3) body.data to the new body.
        '''
        request['body']['action'] = 'replace'
        request['body']['encoding'] = 'text'
        request['body']['data'] = getUpdatedBody(request)
    return request

def getUpdatedBody(request):
    # HTTP body is always passed as base64-encoded string. Decode it
    body = base64.b64decode(request['body']['data'])

    # HTML forms send data in query string format. Parse it
    params = {k: v[0] for k, v in parse_qs(body).items()}

    # For demonstration purposes, we're adding one more param

    # You can put your custom logic here. For example, you can truncate long
    # bodies from malicious requests
    params['new-param-name'] = 'new-param-value'
    return urlencode(params)
```

------