Question: How do I troubleshoot issues related to a signed URL or signed cookies in CloudFront?

I'm using Amazon CloudFront and a signed URL or signed cookies to secure private content. However, I'm receiving a 403 Access Denied error.

ResolutionIf Amazon CloudFront encounters an issue with a signed URL or signed cookies, then you might receive a 403 Access Denied error. A signed URL or signed cookie doesn't include the correct signerWhen you turn on Restrict Viewer Access in a behavior, you must determine a signer. A signer is either a trusted key group that you create in CloudFront, or an AWS account that contains a CloudFront key pair. The following 403 error messages indicate that the signer information is missing or incorrect:The error includes the message "Missing Key-Pair-Id query parameter or cookie value."This message indicates a missing or empty Key-Pair-Id query parameter when using a signed URL. Or, it indicates a missing CloudFront-Key-Pair-ID query string parameter in a signed cookie.The error includes the message "Unknown Key."This message indicates that CloudFront can't verify signer information through Key-Pair-ID (for signed URLs) or CloudFront-Key-Pair-ID (for signed cookies). To resolve this issue, confirm that the correct Key-Pair-ID value is used for a signed URL or CloudFront-Key-Pair-ID for signed cookies:If you use a signed URL, then find and note the value of Key-Pair-ID.-or-If you use signed cookies, then find and note the value of CloudFront-Key-Pair-ID.Then, find the Key ID and confirm that it matches the Key-Pair-ID or CloudFront-Key-Pair-ID:1.    Open the CloudFront console. In the navigation menu, choose Distributions.2.    Select your distribution. Then, choose the Behaviors tab.3.    Select the behavior name, and then choose Edit.4.    Find the Restrict viewer access setting. Note: If it's set to Yes, then requests for files that match the path pattern of this cache behavior must use the signed URL or signed cookie.5.    After you confirm that the Restrict view access field is set to Yes, check the Trusted authorization type field.6.    If the Trusted authorization type value setting is Trusted key groups, then note the name of the trust key group.Find the public key IDs for a trust key group:Open the CloudFront console. Choose Key Groups.In the list of key groups, choose the name of the trusted key group that you noted.Confirm that the Key-Pair-Id or CloudFront-Key-Pair-Id value that you noted in step 1 matches one of the public key IDs in the trusted key group.7.    If the value of Trusted authorization type is Trusted Signer, then CloudFront uses the credentials that AWS generates. In this case, the value of Key-Pair-Id or CloudFront-Key-Pair-Id that you noted in step 1 must match the Access Key ID of the CloudFront credentials. To find the Access Key ID of CloudFront credentials, see Creating key pairs for your signers.A signed URL or signed cookie isn't sent at a valid timeWhen you create a signed URL or signed cookie, a policy statement in JSON format specifies the restrictions on the signed URL. This statement determines how long the URL is valid. CloudFront returns a 403 Access Denied error in any of the following scenarios:A signed URL is sent at time that's greater than the Expires value in a signed URL that uses a canned policy.A signed cookie is sent at a time that's greater than the CloudFront-Expires value in a signed cookie that uses a canned policy.A signed URL or a signed cookie is sent at time that's greater than the DateLessThan value in a custom policy. Or, it's sent at a time that's less than the DateGreaterThan value.Note: The Expires, CloudFront-Expires, DateLessThan, and DateGreaterThan values are in Unix time format (in seconds) and Coordinated Universal Time (UTC). For example, January 1, 2013 10:00 AM UTC converts to 1357034400 in Unix time format. If you use epoch time, then use a 32-bit integer for a date that's no later than 2147483647 (January 19, 2038 at 03:14:07 UTC).The Policy parameter in a signed URL or the CloudFront-Policy attribute in a signed cookie indicates that you use a custom policy. The policy statement is in JSON format and is base64 encoded. To find out the DateLessThan or DateGreaterThan value, use a 64base decode command.Example of a base64 encoded custom policy:eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2QxMTExMTFhYmNkZWY4LmNsb3VkZnJvbnQubmV0L2dhbWVfZG93bmxvYWQuemlwIiwiQ29uZGl0aW9uIjp7IklwQWRkcmVzcyI6eyJBV1M6U291cmNlSXAiOiIxOTIuMC4yLjAvMjQifSwiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE0MjY1MDAwMDB9fX1dfQ__To decode a custom policy that's in base64 encoded format into JSON format, run the following Linux command. This example uses the value from the previous example. Replace the policy value with your own custom policy value.$ echo -n eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2QxMTExMTFhYmNkZWY4LmNsb3VkZnJvbnQubmV0L2dhbWVfZG93bmxvYWQuemlwIiwiQ29uZGl0aW9uIjp7IklwQWRkcmVzcyI6eyJBV1M6U291cmNlSXAiOiIxOTIuMC4yLjAvMjQifSwiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE0MjY1MDAwMDB9fX1dfQ__ | base64 -diYou receive an output that looks similar to the following message:{ "Statement": [{ "Resource": "http://d111111abcdef8.cloudfront.net/game_download.zip", "Condition": { "IpAddress": { "AWS:SourceIp": "192.0.2.0/24" }, "DateLessThan": { "AWS:EpochTime": 1426500000 } } }] }A signed URL or signed cookie has more than one statement in the policyIf more than one statement is included in a canned policy or custom policy, then CloudFront returns a 403 Access Denied error.To troubleshoot this issue, run the Linux command in the previous section to check the custom policy statement. Verify your code details, and then confirm that only one statement is included in the canned policy or custom policy.A signed URL or signed cookie has an incorrect base URL in the policyCloudFront returns a 403 Access Denied error in any of the following scenarios:The base URL is abbreviated (www.example.com) in the Resource key in the policy statement. Use a full URL (http://www.example.com).The base URL doesn't have UTF-8 character encoding.The base URL doesn't include all punctuation and parameter names.The HTTP or HTTPS protocol in the base URL doesn't match the protocol that's used in a request that's sending a signed URL or signed cookies.The base URL's domain name doesn't match the host header that's the user agent uses to send a signed URL or signed cookies.The base URL query string includes characters that aren't valid.A signed URL or signed cookie has an incorrect signature in the policyCloudFront returns a 403 Access Denied error in any of the following scenarios:The policy statement includes white spaces (including tabs and newline characters).The canned policy or custom policy isn't formatted as string before it's hashed. This happens if you create a signed URL or signed cookie without using an AWS SDK.The policy isn't hashed before it generates the signature. This happens if you create the signed URL or signed cookie without using an AWS SDK.For signature best practices when using a signed URL or signed cookies, see Code examples for creating a signature for a signed URL.A signed URL or signed cookie was sent from an IP address or IP range that's not permitted in the custom policyCloudFront returns a 403 Access Denied error in any of the following scenarios:A signed URL or signed cookie is sent from an IPv6 IP address.A signed URL or signed cookie isn't sent from an IPv4 IP address or an IPv4 IP range that's specified in the custom policy.The IpAddress key is available only in the custom policy that's in a signed URL or a signed cookie. IP addresses in IPv6 format aren't supported. If you use a custom policy that includes IpAddress, then don't turn on IPv6 for the distribution.A signed cookie doesn't include the Domain and Path attributes in Set-cookie response headersCloudFront returns a 403 Access Denied error if cookies return from CloudFront but aren't included in later requests to the same domain. In this case, check the Domain and Path cookie attributes in the Set-Cookie response header.The Domain value is the domain name for the requested file. If you don't specify a Domain attribute, then the default value is the domain name in the URL. This applies only to the specified domain name, not to subdomains. If you specify a Domain attribute, then it also applies to subdomains.If you specify a Domain attribute, then the domain name in the URL and the value of the Domain attribute must match. You can specify the domain name that CloudFront assigns to your distribution (for example, d111111abcdef8.cloudfront.net), but you can't specify *.cloudfront.net for the domain name. To use an alternate domain name, such as example.com, in URLs, add an alternate domain name to your distribution.The Path attribute is the path for the requested file. If you don't specify a Path attribute, then the default value is the path in the URL.