

# Use signed URLs
<a name="private-content-signed-urls"></a>

A signed URL includes additional information, for example, an expiration date and time, that gives you more control over access to your content. This additional information appears in a policy statement, which is based on either a canned policy or a custom policy. The differences between canned and custom policies are explained in the next two sections.

**Note**  
You can create some signed URLs using canned policies and create some signed URLs using custom policies for the same distribution.

**Topics**
+ [Decide to use canned or custom policies for signed URLs](#private-content-choosing-canned-custom-policy)
+ [How signed URLs work](#private-content-how-signed-urls-work)
+ [Decide how long signed URLs are valid](#private-content-overview-choosing-duration)
+ [When CloudFront checks expiration date and time in a signed URL](#private-content-check-expiration)
+ [Example code and third-party tools](#private-content-overview-sample-code)
+ [Create a signed URL using a canned policy](private-content-creating-signed-url-canned-policy.md)
+ [Create a signed URL using a custom policy](private-content-creating-signed-url-custom-policy.md)

## Decide to use canned or custom policies for signed URLs
<a name="private-content-choosing-canned-custom-policy"></a>

When you create a signed URL, you write a policy statement in JSON format that specifies the restrictions on the signed URL, for example, how long the URL is valid. You can use either a canned policy or a custom policy. Here's how canned and custom policies compare:


****  

| Description | Canned policy | Custom policy | 
| --- | --- | --- | 
| You can reuse the policy statement for multiple files. To reuse the policy statement, you must use wildcard characters in the `Resource` object. For more information, see [Values that you specify in the policy statement for a signed URL that uses a custom policy](private-content-creating-signed-url-custom-policy.md#private-content-custom-policy-statement-values).)  | No | Yes | 
| You can specify the date and time that users can begin to access your content. | No | Yes (optional) | 
| You can specify the date and time that users can no longer access your content. | Yes | Yes | 
| You can specify the IP address or range of IP addresses of the users who can access your content. | No | Yes (optional) | 
| The signed URL includes a base64-encoded version of the policy, which results in a longer URL. | No | Yes | 

For information about creating signed URLs using a *canned* policy, see [Create a signed URL using a canned policy](private-content-creating-signed-url-canned-policy.md).

For information about creating signed URLs using a *custom* policy, see [Create a signed URL using a custom policy](private-content-creating-signed-url-custom-policy.md).

## How signed URLs work
<a name="private-content-how-signed-urls-work"></a>

Here's an overview of how you configure CloudFront and Amazon S3 for signed URLs and how CloudFront responds when a user uses a signed URL to request a file. 

1. In your CloudFront distribution, specify one or more trusted key groups, which contain the public keys that CloudFront can use to verify the URL signature. You use the corresponding private keys to sign the URLs.

   CloudFront supports signed URLs with RSA 2048 and ECDSA 256 key signatures.

   For more information, see [Specify signers that can create signed URLs and signed cookies](private-content-trusted-signers.md).

1. Develop your application to determine whether a user should have access to your content and to create signed URLs for the files or parts of your application that you want to restrict access to. For more information, see the following topics:
   + [Create a signed URL using a canned policy](private-content-creating-signed-url-canned-policy.md)
   + [Create a signed URL using a custom policy](private-content-creating-signed-url-custom-policy.md)

1. A user requests a file for which you want to require signed URLs.

1. Your application verifies that the user is entitled to access the file: they've signed in, they've paid for access to the content, or they've met some other requirement for access.

1. Your application creates and returns a signed URL to the user.

1. The signed URL allows the user to download or stream the content.

   This step is automatic; the user usually doesn't have to do anything additional to access the content. For example, if a user is accessing your content in a web browser, your application returns the signed URL to the browser. The browser immediately uses the signed URL to access the file in the CloudFront edge cache without any intervention from the user.

1. CloudFront uses the public key to validate the signature and confirm that the URL hasn't been tampered with. If the signature is invalid, the request is rejected. 

   If the signature is valid, CloudFront looks at the policy statement in the URL (or constructs one if you're using a canned policy) to confirm that the request is still valid. For example, if you specified a beginning and ending date and time for the URL, CloudFront confirms that the user is trying to access your content during the time period that you want to allow access. 

   If the request meets the requirements in the policy statement, CloudFront does the standard operations: determines whether the file is already in the edge cache, forwards the request to the origin if necessary, and returns the file to the user.

**Note**  
If an unsigned URL contains query string parameters, make sure you include them in the portion of the URL that you sign. If you add a query string to a signed URL after signing it, the URL returns an HTTP 403 status.

## Decide how long signed URLs are valid
<a name="private-content-overview-choosing-duration"></a>

You can distribute private content using a signed URL that is valid for only a short time—possibly for as little as a few minutes. Signed URLs that are valid for such a short period are good for distributing content on-the-fly to a user for a specific purpose, such as distributing movie rentals or music downloads to customers on demand. If your signed URLs will be valid for just a short period, you'll probably want to generate them automatically using an application that you develop. When the user starts to download a file or starts to play a media file, CloudFront compares the expiration time in the URL with the current time to determine whether the URL is still valid.

You can also distribute private content using a signed URL that is valid for a longer time, possibly for years. Signed URLs that are valid for a longer period are useful for distributing private content to known users, such as distributing a business plan to investors or distributing training materials to employees. You can develop an application to generate these longer-term signed URLs for you.

## When CloudFront checks expiration date and time in a signed URL
<a name="private-content-check-expiration"></a>

CloudFront checks the expiration date and time in a signed URL at the time of the HTTP request. If a client begins to download a large file immediately before the expiration time, the download should complete even if the expiration time passes during the download. If the TCP connection drops and the client tries to restart the download after the expiration time passes, the download will fail.

If a client uses Range GETs to get a file in smaller pieces, any GET request that occurs after the expiration time passes will fail. For more information about Range GETs, see [How CloudFront processes partial requests for an object (range GETs)](RangeGETs.md).

## Example code and third-party tools
<a name="private-content-overview-sample-code"></a>

For example code that creates the hashed and signed part of signed URLs, see the following topics:
+ [Create a URL signature using Perl](CreateURLPerl.md)
+ [Create a URL signature using PHP](CreateURL_PHP.md)
+ [Create a URL signature using C\$1 and the .NET Framework](CreateSignatureInCSharp.md)
+ [Create a URL signature using Java](CFPrivateDistJavaDevelopment.md)

# Create a signed URL using a canned policy
<a name="private-content-creating-signed-url-canned-policy"></a>

To create a signed URL using a canned policy, complete the following steps.<a name="private-content-creating-signed-url-canned-policy-procedure"></a>

**To create a signed URL using a canned policy**

1. If you're using .NET or Java to create signed URLs, and if you haven't reformatted the private key for your key pair from the default .pem format to a format compatible with .NET or with Java, do so now. For more information, see [Reformat the private key (.NET and Java only)](private-content-trusted-signers.md#private-content-reformatting-private-key).

1. Concatenate the following values. You can use the format in this example signed URL. 

   ```
   https://d111111abcdef8.cloudfront.net/image.jpg?color=red&size=medium&Expires=1767290400&Signature=nitfHRCrtziwO2HwPfWw~yYDhUF5EwRunQA-j19DzZrvDh6hQ73lDx~-ar3UocvvRQVw6EkC~GdpGQyyOSKQim-TxAnW7d8F5Kkai9HVx0FIu-5jcQb0UEmatEXAMPLE3ReXySpLSMj0yCd3ZAB4UcBCAqEijkytL6f3fVYNGQI6&Key-Pair-Id=K2JCJMDEHXQW5F&Hash-Algorithm=SHA256
   ```

   Remove all empty spaces (including tabs and newline characters). You might have to include escape characters in the string in application code. All values have a type of `String`.  
**1. *Base URL for the file***  
The base URL is the CloudFront URL that you would use to access the file if you were not using signed URLs, including your own query string parameters, if any. In the preceding example, the base URL is `https://d111111abcdef8.cloudfront.net/image.jpg`. For more information about the format of URLs for distributions, see [Customize the URL format for files in CloudFront](LinkFormat.md).  
   + The following CloudFront URL is for an image file in a distribution (using the CloudFront domain name). Note that `image.jpg` is in an `images` directory. The path to the file in the URL must match the path to the file on your HTTP server or in your Amazon S3 bucket.

     `https://d111111abcdef8.cloudfront.net/images/image.jpg`
   + The following CloudFront URL includes a query string:

     `https://d111111abcdef8.cloudfront.net/images/image.jpg?size=large`
   + The following CloudFront URLs are for image files in a distribution. Both use an alternate domain name. The second one includes a query string:

     `https://www.example.com/images/image.jpg`

     `https://www.example.com/images/image.jpg?color=red`
   + The following CloudFront URL is for an image file in a distribution that uses an alternate domain name and the HTTPS protocol:

     `https://www.example.com/images/image.jpg`  
** 2. `?`**  
The `?` indicates that query parameters follow the base URL. Include the `?` even if you don't specify any query parameters.  
You can specify the following query parameters in any order.  
**3. *Your query string parameters, if any*`&`**  
(Optional) You can enter your own query string parameters. To do so, add an ampersand (`&`) between each one, such as `color=red&size=medium`. You can specify query string parameters in any order within the URL.  
Your query string parameters can't be named `Expires`, `Signature`, `Key-Pair-Id`, or `Hash-Algorithm`.  
** 4. `Expires=`*date and time in Unix time format (in seconds) and Coordinated Universal Time (UTC)***  
The date and time that you want the URL to stop allowing access to the file.  
Specify the expiration date and time in Unix time format (in seconds) and Coordinated Universal Time (UTC). For example, January 1, 2026 10:00 am UTC converts to `1767290400` in Unix time format, as shown in the example at the start of this topic.   
To use epoch time, specify a 64-bit integer for a date that's no later than `9223372036854775807` (Friday, April 11, 2262 at 23:47:16.854 UTC).  
  
For information about UTC, see [RFC 3339, Date and Time on the Internet: Timestamps](https://tools.ietf.org/html/rfc3339).  
** 5. `&Signature=`*hashed and signed version of the policy statement***  
A hashed, signed, and base64-encoded version of the JSON policy statement. For more information, see [Create a signature for a signed URL that uses a canned policy](#private-content-canned-policy-creating-signature).  
** 6. `&Key-Pair-Id=`*public key ID for the CloudFront public key whose corresponding private key you're using to generate the signature***  
The ID for a CloudFront public key, for example, `K2JCJMDEHXQW5F`. The public key ID tells CloudFront which public key to use to validate the signed URL. CloudFront compares the information in the signature with the information in the policy statement to verify that the URL has not been tampered with.  
This public key must belong to a key group that is a trusted signer in the distribution. For more information, see [Specify signers that can create signed URLs and signed cookies](private-content-trusted-signers.md).  
** 7. `&Hash-Algorithm=`*SHA1 or SHA256***  
(Optional) The hash algorithm used to create the signature. Supported values are `SHA1` and `SHA256`. If you don't specify this parameter, CloudFront defaults to `SHA1`.

## Create a signature for a signed URL that uses a canned policy
<a name="private-content-canned-policy-creating-signature"></a>

To create the signature for a signed URL that uses a canned policy, complete the following procedures.

**Topics**
+ [Create a policy statement for a signed URL that uses a canned policy](#private-content-canned-policy-creating-policy-statement)
+ [Create a signature for a signed URL that uses a canned policy](#private-content-canned-policy-signing-policy-statement)

### Create a policy statement for a signed URL that uses a canned policy
<a name="private-content-canned-policy-creating-policy-statement"></a>

When you create a signed URL using a canned policy, the `Signature` parameter is a hashed and signed version of a policy statement. For signed URLs that use a canned policy, you don't include the policy statement in the URL, as you do for signed URLs that use a custom policy. To create the policy statement, do the following procedure.<a name="private-content-canned-policy-creating-policy-statement-procedure"></a>

**To create the policy statement for a signed URL that uses a canned policy**

1. Construct the policy statement using the following JSON format and using UTF-8 character encoding. Include all punctuation and other literal values exactly as specified. For information about the `Resource` and `DateLessThan` parameters, see [Values that you specify in the policy statement for a signed URL that uses a canned policy](#private-content-canned-policy-statement-values).

   ```
   {
       "Statement": [
           {
               "Resource": "base URL or stream name",
               "Condition": {
                   "DateLessThan": {
                       "AWS:EpochTime": ending date and time in Unix time format and UTC
                   }
               }
           }
       ]
   }
   ```

1. Remove all empty spaces (including tabs and newline characters) from the policy statement. You might have to include escape characters in the string in application code.

#### Values that you specify in the policy statement for a signed URL that uses a canned policy
<a name="private-content-canned-policy-statement-values"></a>

When you create a policy statement for a canned policy, you specify the following values.

**Resource**  
You can specify only one value for `Resource`.
The base URL including your query strings, if any, but excluding the CloudFront `Expires`, `Signature`, `Key-Pair-Id`, and `Hash-Algorithm` parameters, for example:  
`https://d111111abcdef8.cloudfront.net/images/horizon.jpg?size=large&license=yes`  
Note the following:  
+ **Protocol** – The value must begin with `http://` or `https://`.
+ **Query string parameters** – If you have no query string parameters, omit the question mark.
+ **Alternate domain names** – If you specify an alternate domain name (CNAME) in the URL, you must specify the alternate domain name when referencing the file in your webpage or application. Do not specify the Amazon S3 URL for the object.

**DateLessThan**  
The expiration date and time for the URL in Unix time format (in seconds) and Coordinated Universal Time (UTC). For example, January 1, 2026 10:00 am UTC converts to 1767290400 in Unix time format.  
This value must match the value of the `Expires` query string parameter in the signed URL. Do not enclose the value in quotation marks.  
For more information, see [When CloudFront checks expiration date and time in a signed URL](private-content-signed-urls.md#private-content-check-expiration).

#### Example policy statement for a signed URL that uses a canned policy
<a name="private-content-canned-policy-creating-policy-statement-example"></a>

When you use the following example policy statement in a signed URL, a user can access the file `https://d111111abcdef8.cloudfront.net/horizon.jpg` until January 1, 2026 10:00 am UTC:

```
{
    "Statement": [
        {
            "Resource": "https://d111111abcdef8.cloudfront.net/horizon.jpg?size=large&license=yes",
            "Condition": {
                "DateLessThan": {
                    "AWS:EpochTime": 1767290400
                }
            }
        }
    ]
}
```

### Create a signature for a signed URL that uses a canned policy
<a name="private-content-canned-policy-signing-policy-statement"></a>

To create the value for the `Signature` parameter in a signed URL, you hash and sign the policy statement that you created in [Create a policy statement for a signed URL that uses a canned policy](#private-content-canned-policy-creating-policy-statement).

For additional information and examples of how to hash, sign, and encode the policy statement, see:
+ [Linux commands and OpenSSL for base64 encoding and encryption](private-content-linux-openssl.md)
+ [Code examples for creating a signature for a signed URL](PrivateCFSignatureCodeAndExamples.md)

**Note**  
The linked examples use SHA-1 by default. To use SHA-256 instead, replace `sha1` with `sha256` in the OpenSSL commands and include the `Hash-Algorithm=SHA256` query parameter in the signed URL.<a name="private-content-canned-policy-creating-signature-download-procedure"></a>

**Option 1: To create a signature by using a canned policy**

1. Use the SHA-1 or SHA-256 hash function and the generated RSA or ECDSA private key to hash and sign the policy statement that you created in the procedure [To create the policy statement for a signed URL that uses a canned policy](#private-content-canned-policy-creating-policy-statement-procedure). Use the version of the policy statement that no longer includes empty spaces.

   If you use SHA-256, you must include `&Hash-Algorithm=SHA256` in the signed URL.

   For the private key that is required by the hash function, use a private key whose public key is in an active trusted key group for the distribution.
**Note**  
The method that you use to hash and sign the policy statement depends on your programming language and platform. For sample code, see [Code examples for creating a signature for a signed URL](PrivateCFSignatureCodeAndExamples.md).

1. Remove empty spaces (including tabs and newline characters) from the hashed and signed string.

1. Base64-encode the string using MIME base64 encoding. For more information, see [Section 6.8, Base64 Content-Transfer-Encoding](https://tools.ietf.org/html/rfc2045#section-6.8) in *RFC 2045, MIME (Multipurpose Internet Mail Extensions) Part One: Format of Internet Message Bodies*.

1. Replace characters that are invalid in a URL query string with characters that are valid. The following table lists invalid and valid characters.  
****    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-canned-policy.html)

1. Append the resulting value to your signed URL after `&Signature=`, and return to [To create a signed URL using a canned policy](#private-content-creating-signed-url-canned-policy-procedure) to finish concatenating the parts of your signed URL.

# Create a signed URL using a custom policy
<a name="private-content-creating-signed-url-custom-policy"></a>

To create a signed URL using a custom policy, complete the following procedure.<a name="private-content-creating-signed-url-custom-policy-procedure"></a>

**To create a signed URL using a custom policy**

1. If you're using .NET or Java to create signed URLs, and if you haven't reformatted the private key for your key pair from the default .pem format to a format compatible with .NET or with Java, do so now. For more information, see [Reformat the private key (.NET and Java only)](private-content-trusted-signers.md#private-content-reformatting-private-key).

1. Concatenate the following values. You can use the format in this example signed URL.

   

   ```
   https://d111111abcdef8.cloudfront.net/image.jpg?color=red&size=medium&Policy=eyANCiAgICEXAMPLEW1lbnQiOiBbeyANCiAgICAgICJSZXNvdXJjZSI6Imh0dHA6Ly9kemJlc3FtN3VuMW0wLmNsb3VkZnJvbnQubmV0L2RlbW8ucGhwIiwgDQogICAgICAiQ29uZGl0aW9uIjp7IA0KICAgICAgICAgIklwQWRkcmVzcyI6eyJBV1M6U291cmNlSXAiOiIyMDcuMTcxLjE4MC4xMDEvMzIifSwNCiAgICAgICAgICJEYXRlR3JlYXRlclRoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTI5Njg2MDE3Nn0sDQogICAgICAgICAiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjEyOTY4NjAyMjZ9DQogICAgICB9IA0KICAgfV0gDQp9DQo&Signature=nitfHRCrtziwO2HwPfWw~yYDhUF5EwRunQA-j19DzZrvDh6hQ73lDx~-ar3UocvvRQVw6EkC~GdpGQyyOSKQim-TxAnW7d8F5Kkai9HVx0FIu-5jcQb0UEmatEXAMPLE3ReXySpLSMj0yCd3ZAB4UcBCAqEijkytL6f3fVYNGQI6&Key-Pair-Id=K2JCJMDEHXQW5F&Hash-Algorithm=SHA256
   ```

   Remove all empty spaces (including tabs and newline characters). You might have to include escape characters in the string in application code. All values have a type of `String`.  
**1. *Base URL for the file***  
The base URL is the CloudFront URL that you would use to access the file if you were not using signed URLs, including your own query string parameters, if any. In the preceding example, the base URL is `https://d111111abcdef8.cloudfront.net/image.jpg`. For more information about the format of URLs for distributions, see [Customize the URL format for files in CloudFront](LinkFormat.md).  
The following examples show values that you specify for distributions.  
   + The following CloudFront URL is for an image file in a distribution (using the CloudFront domain name). Note that `image.jpg` is in an `images` directory. The path to the file in the URL must match the path to the file on your HTTP server or in your Amazon S3 bucket.

     `https://d111111abcdef8.cloudfront.net/images/image.jpg`
   + The following CloudFront URL includes a query string:

     `https://d111111abcdef8.cloudfront.net/images/image.jpg?size=large`
   + The following CloudFront URLs are for image files in a distribution. Both use an alternate domain name; the second one includes a query string:

     `https://www.example.com/images/image.jpg`

     `https://www.example.com/images/image.jpg?color=red`
   + The following CloudFront URL is for an image file in a distribution that uses an alternate domain name and the HTTPS protocol:

     `https://www.example.com/images/image.jpg`  
**2. `?`**  
The `?` indicates that query string parameters follow the base URL. Include the `?` even if you don't specify any query parameters.  
You can specify the following query parameters in any order.  
**3. *Your query string parameters, if any*`&`**  
(Optional) You can enter your own query string parameters. To do so, add an ampersand (&) between each one, such as `color=red&size=medium`. You can specify query string parameters in any order within the URL.  
Your query string parameters can't be named `Policy`, `Signature`, `Key-Pair-Id`, or `Hash-Algorithm`.
If you add your own parameters, append an `&` after each one, including the last one.   
**4. `Policy=`*base64 encoded version of policy statement***  
Your policy statement in JSON format, with empty spaces removed, then base64 encoded. For more information, see [Create a policy statement for a signed URL that uses a custom policy](#private-content-custom-policy-statement).  
The policy statement controls the access that a signed URL grants to a user. It includes the URL of the file, an expiration date and time, an optional date and time that the URL becomes valid, and an optional IP address or range of IP addresses that are allowed to access the file.  
**5. `&Signature=`*hashed and signed version of the policy statement***  
A hashed, signed, and base64-encoded version of the JSON policy statement. For more information, see [Create a signature for a signed URL that uses a custom policy](#private-content-custom-policy-creating-signature).  
**6. `&Key-Pair-Id=`*public key ID for the CloudFront public key whose corresponding private key you're using to generate the signature***  
The ID for a CloudFront public key, for example, `K2JCJMDEHXQW5F`. The public key ID tells CloudFront which public key to use to validate the signed URL. CloudFront compares the information in the signature with the information in the policy statement to verify that the URL has not been tampered with.  
This public key must belong to a key group that is a trusted signer in the distribution. For more information, see [Specify signers that can create signed URLs and signed cookies](private-content-trusted-signers.md).  
**7. `&Hash-Algorithm=`*SHA1 or SHA256***  
(Optional) The hash algorithm used to create the signature. Supported values are `SHA1` and `SHA256`. If you don't specify this parameter, CloudFront defaults to `SHA1`.

## Create a policy statement for a signed URL that uses a custom policy
<a name="private-content-custom-policy-statement"></a>

Complete the following steps to create a policy statement for a signed URL that uses a custom policy.

For example policy statements that control access to files in a variety of ways, see [Example policy statements for a signed URL that uses a custom policy](#private-content-custom-policy-statement-examples).<a name="private-content-custom-policy-creating-policy-procedure"></a>

**To create the policy statement for a signed URL that uses a custom policy**

1. Construct the policy statement using the following JSON format. Replace the less than (`<`) and greater than (`>`) symbols, and the descriptions within them, with your own values. For more information, see [Values that you specify in the policy statement for a signed URL that uses a custom policy](#private-content-custom-policy-statement-values).

   ```
   {
       "Statement": [
           {
               "Resource": "<Optional but recommended: URL of the file>",
               "Condition": {
                   "DateLessThan": {
   	                "AWS:EpochTime": <Required: ending date and time in Unix time format and UTC>
                   },
                   "DateGreaterThan": {
   	                "AWS:EpochTime": <Optional: beginning date and time in Unix time format and UTC>
                   },
                   "IpAddress": {
   	                "AWS:SourceIp": "<Optional: IP address>"
                   }
               }
           }
       ]
   }
   ```

   Note the following:
   + You can include only one statement in the policy.
   + Use UTF-8 character encoding.
   + Include all punctuation and parameter names exactly as specified. Abbreviations for parameter names are not accepted.
   + The order of the parameters in the `Condition` section doesn't matter.
   + For information about the values for `Resource`, `DateLessThan`, `DateGreaterThan`, and `IpAddress`, see [Values that you specify in the policy statement for a signed URL that uses a custom policy](#private-content-custom-policy-statement-values).

1. Remove all empty spaces (including tabs and newline characters) from the policy statement. You might have to include escape characters in the string in application code.

1. Base64-encode the policy statement using MIME base64 encoding. For more information, see [Section 6.8, Base64 Content-Transfer-Encoding](https://tools.ietf.org/html/rfc2045#section-6.8) in *RFC 2045, MIME (Multipurpose Internet Mail Extensions) Part One: Format of Internet Message Bodies*.

1. Replace characters that are invalid in a URL query string with characters that are valid. The following table lists invalid and valid characters.  
****    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-custom-policy.html)

1. Append the resulting value to your signed URL after `Policy=`.

1. Create a signature for the signed URL by hashing, signing, and base64-encoding the policy statement. For more information, see [Create a signature for a signed URL that uses a custom policy](#private-content-custom-policy-creating-signature).

### Values that you specify in the policy statement for a signed URL that uses a custom policy
<a name="private-content-custom-policy-statement-values"></a>

When you create a policy statement for a custom policy, you specify the following values.

**Resource**  
The URL, including any query strings, but excluding the CloudFront `Policy`, `Signature`, `Key-Pair-Id`, and `Hash-Algorithm` parameters. For example:  
`https://d111111abcdef8.cloudfront.net/images/horizon.jpg\?size=large&license=yes`  
You can specify only one URL value for `Resource`.  
You can omit the `Resource` parameter in a policy, but doing so means that anyone with the signed URL can access *all* of the files in *any* distribution that is associated with the key pair that you use to create the signed URL.
Note the following:  
+ **Protocol** – The value must begin with `http://`, `https://`, or `*://`.
+ **Query string parameters** – If the URL has query string parameters, don't use a backslash character (`\`) to escape the question mark character (`?`) that begins the query string. For example:

  `https://d111111abcdef8.cloudfront.net/images/horizon.jpg?size=large&license=yes`
+ **Wildcard characters** – You can use wildcard characters in the URL in the policy. The following wildcard characters are supported:
  + asterisk (`*`), which matches zero or more characters
  + question mark (`?`), which matches exactly one character

  When CloudFront matches the URL in the policy to the URL in the HTTP request, the URL in the policy is divided into four sections—protocol, domain, path, and query string—as follows:

  `[protocol]://[domain]/[path]\?[query string]`

  When you use a wildcard character in the URL in the policy, the wildcard matching applies only within the boundaries of the section that contains the wildcard. For example, consider this URL in a policy:

  `https://www.example.com/hello*world`

  In this example, the asterisk wildcard (`*`) only applies within the path section, so it matches the URLs `https://www.example.com/helloworld` and `https://www.example.com/hello-world`, but it does not match the URL `https://www.example.net/hello?world`.

  The following exceptions apply to the section boundaries for wildcard matching:
  + A trailing asterisk in the path section implies an asterisk in the query string section. For example, `http://example.com/hello*` is equivalent to `http://example.com/hello*\?*`.
  + A trailing asterisk in the domain section implies an asterisk in both the path and query string sections. For example, `http://example.com*` is equivalent to `http://example.com*/*\?*`.
  + A URL in the policy can omit the protocol section and start with an asterisk in the domain section. In that case, the protocol section is implicitly set to an asterisk. For example, the URL `*example.com` in a policy is equivalent to `*://*example.com/`.
  + An asterisk by itself (`"Resource": "*"`) matches any URL.

  For example, the value: `https://d111111abcdef8.cloudfront.net/*game_download.zip*` in a policy matches all of the following URLs:
  + `https://d111111abcdef8.cloudfront.net/game_download.zip`
  + `https://d111111abcdef8.cloudfront.net/example_game_download.zip?license=yes`
  + `https://d111111abcdef8.cloudfront.net/test_game_download.zip?license=temp`
+ **Alternate domain names** – If you specify an alternate domain name (CNAME) in the URL in the policy, the HTTP request must use the alternate domain name in your webpage or application. Do not specify the Amazon S3 URL for the file in a policy.

**DateLessThan**  
The expiration date and time for the URL in Unix time format (in seconds) and Coordinated Universal Time (UTC). In the policy, do not enclose the value in quotation marks. For information about UTC, see [Date and Time on the Internet: Timestamps](https://tools.ietf.org/html/rfc3339).  
For example, January 31, 2023 10:00 AM UTC converts to 1675159200 in Unix time format.  
This is the only required parameter in the `Condition` section. CloudFront requires this value to prevent users from having permanent access to your private content.  
For more information, see [When CloudFront checks expiration date and time in a signed URL](private-content-signed-urls.md#private-content-check-expiration)

**DateGreaterThan (Optional)**  
An optional start date and time for the URL in Unix time format (in seconds) and Coordinated Universal Time (UTC). Users are not allowed to access the file on or before the specified date and time. Do not enclose the value in quotation marks. 

**IpAddress (Optional)**  
The IP address of the client making the HTTP request. Note the following:  
+ To allow any IP address to access the file, omit the `IpAddress` parameter.
+ You can specify either one IP address or one IP address range. You can't use the policy to allow access if the client's IP address is in one of two separate ranges.
+ To allow access from a single IP address, you specify:

  `"`*IPv4 IP address*`/32"`
+ You must specify IP address ranges in standard IPv4 CIDR format (for example, `192.0.2.0/24`). For more information, see [Classless Inter-domain Routing (CIDR): The Internet Address Assignment and Aggregation Plan](https://tools.ietf.org/html/rfc4632).
**Important**  
IP addresses in IPv6 format, such as 2001:0db8:85a3::8a2e:0370:7334, are not supported. 

  If you're using a custom policy that includes `IpAddress`, do not enable IPv6 for the distribution. If you want to restrict access to some content by IP address and support IPv6 requests for other content, you can create two distributions. For more information, see [Enable IPv6 (viewer requests)](DownloadDistValuesGeneral.md#DownloadDistValuesEnableIPv6) in the topic [All distribution settings reference](distribution-web-values-specify.md).

## Example policy statements for a signed URL that uses a custom policy
<a name="private-content-custom-policy-statement-examples"></a>

The following example policy statements show how to control access to a specific file, all of the files in a directory, or all of the files associated with a key pair ID. The examples also show how to control access from an individual IP address or a range of IP addresses, and how to prevent users from using the signed URL after a specified date and time.

If you copy and paste any of these examples, remove any empty spaces (including tabs and newline characters), replace the values with your own values, and include a newline character after the closing brace (`}`).

For more information, see [Values that you specify in the policy statement for a signed URL that uses a custom policy](#private-content-custom-policy-statement-values).

**Topics**
+ [Example policy statement: Access one file from a range of IP addresses](#private-content-custom-policy-statement-example-one-object)
+ [Example policy statement: Access all files in a directory from a range of IP addresses](#private-content-custom-policy-statement-example-all-objects)
+ [Example policy statement: Access all files associated with a key pair ID from one IP address](#private-content-custom-policy-statement-example-one-ip)

### Example policy statement: Access one file from a range of IP addresses
<a name="private-content-custom-policy-statement-example-one-object"></a>

The following example custom policy in a signed URL specifies that a user can access the file `https://d111111abcdef8.cloudfront.net/game_download.zip` from IP addresses in the range `192.0.2.0/24` until January 31, 2023 10:00 AM UTC:

```
{
    "Statement": [
        {
            "Resource": "https://d111111abcdef8.cloudfront.net/game_download.zip",
            "Condition": {
                "IpAddress": {
                    "AWS:SourceIp": "192.0.2.0/24"
                },
                "DateLessThan": {
                    "AWS:EpochTime": 1675159200
                }
            }
        }
    ]
}
```

### Example policy statement: Access all files in a directory from a range of IP addresses
<a name="private-content-custom-policy-statement-example-all-objects"></a>

The following example custom policy allows you to create signed URLs for any file in the `training` directory, as indicated by the asterisk wildcard character (`*`) in the `Resource` parameter. Users can access the file from an IP address in the range `192.0.2.0/24` until January 31, 2023 10:00 AM UTC:

```
{
    "Statement": [
        {
            "Resource": "https://d111111abcdef8.cloudfront.net/training/*",
            "Condition": {
                "IpAddress": {
                    "AWS:SourceIp": "192.0.2.0/24"
                },
                "DateLessThan": {
                    "AWS:EpochTime": 1675159200
                }
            }
        }
    ]
}
```

Each signed URL with which you use this policy has a URL that identifies a specific file, for example:

`https://d111111abcdef8.cloudfront.net/training/orientation.pdf`

### Example policy statement: Access all files associated with a key pair ID from one IP address
<a name="private-content-custom-policy-statement-example-one-ip"></a>

The following example custom policy allows you to create signed URLs for any file associated with any distribution, as indicated by the asterisk wildcard character (`*`) in the `Resource` parameter. The signed URL must use the `https://` protocol, not `http://`. The user must use the IP address `192.0.2.10/32`. (The value `192.0.2.10/32` in CIDR notation refers to a single IP address, `192.0.2.10`.) The files are available only from January 31, 2023 10:00 AM UTC until February 2, 2023 10:00 AM UTC:

```
{
    "Statement": [
       {
            "Resource": "https://*",
            "Condition": {
                "IpAddress": {
                    "AWS:SourceIp": "192.0.2.10/32"
                },
                "DateGreaterThan": {
                    "AWS:EpochTime": 1675159200
                },
                "DateLessThan": {
                    "AWS:EpochTime": 1675332000
                }
            }
        }
    ]
}
```

Each signed URL with which you use this policy has a URL that identifies a specific file in a specific CloudFront distribution, for example:

`https://d111111abcdef8.cloudfront.net/training/orientation.pdf`

The signed URL also includes a key pair ID, which must be associated with a trusted key group in the distribution (d111111abcdef8.cloudfront.net) that you specify in the URL.

## Create a signature for a signed URL that uses a custom policy
<a name="private-content-custom-policy-creating-signature"></a>

The signature for a signed URL that uses a custom policy is a hashed, signed, and base64-encoded version of the policy statement. To create a signature for a custom policy, complete the following steps.

For additional information and examples of how to hash, sign, and encode the policy statement, see:
+ [Linux commands and OpenSSL for base64 encoding and encryption](private-content-linux-openssl.md)
+ [Code examples for creating a signature for a signed URL](PrivateCFSignatureCodeAndExamples.md)

**Note**  
The linked examples use SHA-1 by default. To use SHA-256 instead, replace `sha1` with `sha256` in the OpenSSL commands and include the `Hash-Algorithm=SHA256` query parameter in the signed URL.<a name="private-content-custom-policy-creating-signature-download-procedure"></a>

**Option 1: To create a signature by using a custom policy**

1. Use the SHA-1 or SHA-256 hash function and the generated RSA or ECDSA private key to hash and sign the JSON policy statement that you created in the procedure [To create the policy statement for a signed URL that uses a custom policy](#private-content-custom-policy-creating-policy-procedure). Use the version of the policy statement that no longer includes empty spaces but that has not yet been base64-encoded.

   If you use SHA-256, you must include `&Hash-Algorithm=SHA256` in the signed URL.

   For the private key that is required by the hash function, use a private key whose public key is in an active trusted key group for the distribution.
**Note**  
The method that you use to hash and sign the policy statement depends on your programming language and platform. For sample code, see [Code examples for creating a signature for a signed URL](PrivateCFSignatureCodeAndExamples.md).

1. Remove empty spaces (including tabs and newline characters) from the hashed and signed string.

1. Base64-encode the string using MIME base64 encoding. For more information, see [Section 6.8, Base64 Content-Transfer-Encoding](https://tools.ietf.org/html/rfc2045#section-6.8) in *RFC 2045, MIME (Multipurpose Internet Mail Extensions) Part One: Format of Internet Message Bodies*.

1. Replace characters that are invalid in a URL query string with characters that are valid. The following table lists invalid and valid characters.  
****    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-custom-policy.html)

1. Append the resulting value to your signed URL after `&Signature=`, and return to [To create a signed URL using a custom policy](#private-content-creating-signed-url-custom-policy-procedure) to finish concatenating the parts of your signed URL.