

# Use the solution
<a name="use-the-solution"></a>

This section provides a user guide for utilizing the AWS solution based on your chosen architecture.

# Lambda architecture
<a name="lambda-architecture-usage"></a>

This section covers how to use the solution when deployed with the Lambda architecture.

# Use the demo UI
<a name="use-the-demo-ui"></a>

The solution provides an optional demo UI that you can deploy into your AWS account to display basic capability and functionality. With this UI, you can interact directly with the new image handler using images from the specified Amazon S3 buckets in your account.

 **Screenshot of demo UI showing image source, original image, editing options, preview, code, and encoded URL.** 

![\[demo ui example\]](http://docs.aws.amazon.com/solutions/latest/dynamic-image-transformation-for-amazon-cloudfront/images/demo-ui-example.png)


Follow this procedure to experiment with the supported image editing features, preview the results, and create example URLs that you can use in your applications:

1. Sign in to the [AWS CloudFormation console](https://console.aws.amazon.com/cloudformation/).

1. Select the solution’s installation stack.

1. Choose the **Outputs** tab, and then select value for the **DemoUrl** key. The Dynamic Image Transformation for Amazon CloudFront Demo UI opens in your browser.

1. In the **Image Source** card, perform the following actions:

   1. Specify a bucket name to use for the demo. The bucket you specify must be listed in the `SOURCE_BUCKETS` environment variable of the AWS Lambda function.

   1. Specify an image key to use for the demo. You must include the file extension in the key.

1. Select **Import**. The original image appears in the **Original Image** card.

1. In the **Editor** card, adjust the image settings, and select **Preview** to generate the modified image. You can select **Reset** to revert the settings back to their original values.

**Note**  
The Dynamic Image Transformation for Amazon CloudFront demo UI offers a limited set of image edits and doesn’t include the full scope of capabilities offered by the Image Handler API and the image URL signature. We recommended using your own [frontend application](use-the-solution-with-a-frontend-application.md) for image modification.

# Use the solution with a frontend application
<a name="use-the-solution-with-a-frontend-application"></a>

In your frontend application, you can access both the original and modified images by creating an image request object, stringifying and encoding that object, and appending it to the API call. Follow these steps to retrieve your API endpoint for the solution:

1. Sign in to the [AWS CloudFormation console](https://console.aws.amazon.com/cloudformation/home?).

1. On the **Stacks** page, select this solution’s installation stack.

1. Choose the **Outputs** tab. The domain name appears as the value for the **ApiEndpoint** key. This URL is the endpoint URL for your newly provisioned image handler API.

To use the solution with your frontend application, use the following example syntax for the API call:

```
https://<ApiEndpoint>/<encodedRequest>
```

# Create and use image requests
<a name="create-and-use-image-requests"></a>

This solution generates a CloudFront domain name that gives you access to both original and modified images through the image handler API. You can specify parameters such as the image’s location and edits to be made in a JSON object on the frontend.

Follow these step-by-step instructions to create image requests:

**Note**  
The following image formats are supported for modifications: JPG/JPEG, PNG, TIFF/TIF, WEBP, GIF and 8-bit AVIF. For retrieval, the following formats are supported: All those listed previously, as well as AVIF (all bit depths) and SVG. Edited SVG files will be converted to .png by default.

1. Retrieve your API endpoint for the solution. Refer to [Use the solution with a frontend application](use-the-solution-with-a-frontend-application.md) for instructions.

1. In a code sandbox, or in your frontend application, create a new `imageRequest` JSON object. This object contains the key-value pairs needed to successfully retrieve and perform edits on your images. Using the following code sample and the [sharp](https://sharp.pixelplumbing.com/) documentation, adjust the following properties to meet your image editing requirements.
   +  **Bucket** - Specify the S3 bucket containing your original image file. This is the name that’s specified in the **SourceBuckets** template parameter. You can update the image location by adding it into the SOURCE\$1BUCKETS environment variable of your image handler Lambda function. See [Using AWS Lambda environment variables](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html) in the *AWS Lambda Developer Guide* for more information.
   +  **Key** - Specify the filename of your original image. This name should include the file extension and subfolders between its location and the root of the bucket. For example, `folder1/folder2/image.jpeg`.
   +  **Edits** - Specify image edits as key-value pairs. If you don’t specify image edits, the original image returns with no changes made.

   For example, the following code block specifies the image location as `myImageBucket` and specifies edits of `grayscale: true` to change the image to grayscale:

```
const imageRequest = JSON.stringify({
    bucket: "<myImageBucket>",
    key: "<myImage.jpeg>",
    edits: {
        grayscale: true
    }
})
```

1. Stringify the JSON request object. For example:

   ```
   const stringifiedObject = JSON.stringify(<myObject>);
   ```

1. Base64 encode the JSON string. For example:

   ```
   const encodedObject = btoa(<stringifiedObject>);
   ```

1. Append the encoded string onto the CloudFront URL. For example:

   ```
   const url = '${<ApiEndpoint>}/${<encodedObject>}';
   ```

1. Use that URL either in the JavaScript as part of a `GET` request, or in the frontend as part of an HTML `img` tag’s `src` property.

For information regarding how to use additional features in an image request, refer to [Dynamically resize photos](dynamically-resize-photos.md), [Use smart cropping](use-smart-cropping.md), [Use round cropping](use-round-cropping.md), and [Activate and use content moderation](activate-and-use-content-moderation.md). For additional features supported by `sharp`, refer to the [sharp](https://sharp.pixelplumbing.com/) documentation.

**Note**  
The following filters are not supported for multi-page GIF images due to limitations in the underlying libraries: **rotate**, **smartCrop**, **roundCrop**, and **contentModeration**.

# Dynamically resize photos
<a name="dynamically-resize-photos"></a>

This solution offers the following **fit** options to dynamically resize an image: `cover`, `contain`, `fill`, `inside`, and `outside`. Refer to the [sharp documentation](https://sharp.pixelplumbing.com/api-resize) for a description of each fit. For example:

```
const imageRequest = JSON.stringify({
    bucket: "<myImageBucket>",
    key: "<myImage.jpeg>",
    edits: {
        resize: {
            width: 200,
            height: 250,
            fit: "cover"
        }
    }
})
```

If you use `contain` as the resize **fit** mode, you can specify the color of the fill by providing the hex code of the color you want to use. For example:

```
const imageRequest = JSON.stringify({
    bucket: "<myImageBucket>",
    key: "<myImage.jpeg>",
    edits: {
        resize: {
            width: 200,
            height: 250,
            fit: "contain",
            background: {
                r: 255,
                g: 0,
                b: 0,
                alpha: 1
            }
        }
    }
})
```

# Edit images
<a name="edit-images"></a>

You can use this solution to edit your images, such as rotating them or changing the coloring to negative. Refer to the [sharp documentation](https://sharp.pixelplumbing.com/api-operation) for a description of each operation. For example, to produce a negative of an image, enter the following:

```
const imageRequest = JSON.stringify({
    bucket: "<myImageBucket>",
    key: "<myImage.jpeg>",
    edits: {
        negate: true
    }
})
```

# Restricted operations
<a name="restricted-operations"></a>

Certain Sharp operations are restricted by the solution to help enhance security. This includes (but may not be limited to):
+ clone
+ metadata
+ stats
+ composite (Though this is permitted through the use of overlayWith)
+ certain [output options](https://sharp.pixelplumbing.com/api-output) (Including toFile, toBuffer, tile and raw)

For an exact list of allow-listed Sharp operations, you can visit [constants.ts](https://github.com/aws-solutions/serverless-image-handler/blob/main/source/image-handler/lib/constants.ts) on the Solution GitHub repository.

# Use smart cropping
<a name="use-smart-cropping"></a>

This solution uses Amazon Rekognition for face detection in images submitted for smart cropping. To activate smart cropping on an image, add the **smartCrop** property to the **edits** property in the [image request](use-the-solution-with-a-frontend-application.md).
+  **smartCrop(optional, boolean \$1\$1 object)** - Activates the smart cropping feature for an original image. If the value is `true`, then the feature returns the first face detected from the original image with no additional options. For example:

  ```
  const imageRequest = JSON.stringify({
      bucket: "<myImageBucket>",
      key: "<myImage.jpeg>",
      edits: {
          smartCrop: true
      }
  })
  ```

  The following **smartCrop** variables are shown in the following code sample:

   **smartCrop.faceIndex(optional, number)** - Specifies which face to focus on if multiple are present within an original image. The solution indexes detected faces in a zero-based array from the largest detected face to the smallest. If this value isn’t specified, Amazon Rekognition returns the largest face detected from the original image. **smartCrop.padding(optional, number)** - Specifies an amount of padding in pixels to add around the cropped image. The solution applies the padding value to all sides of the cropped image.

  ```
  const imageRequest = JSON.stringify({
      bucket: "<myImageBucket>",
      key: "<myImage.jpeg>",
      edits: {
          smartCrop: {
              faceIndex: 1,  // zero-based index of detected faces
              padding: 40,   // padding expressed in pixels, applied to all sides
          }
      }
  })
  ```

**Note**  
 **smartCrop** is not supported for animated (such as, GIF) images.

# Use round cropping
<a name="use-round-cropping"></a>

This solution can crop images in a circular pattern. To activate round cropping on an image, add the **roundCrop** property to the **edits** property in the [image request](use-the-solution-with-a-frontend-application.md).
+  **roundCrop(optional, boolean \$1\$1 object)** - Activates the round cropping feature for an original image. If the value is true, then the feature returns a circular cropped image that’s centered from the original image and has a diameter of the smallest edge of the original image. For example:

  ```
  const imageRequest = JSON.stringify({
      bucket: "<myImageBucket>",
      key: "<myImage.jpeg>",
      edits: {
          roundCrop: true
      }
  })
  ```

  The following **roundCrop** variables are shown in the following code sample:

   **roundCrop.rx (optional, number)** - Specifies the radius along the x-axis of the ellipse. If a value isn’t provided, the image handler defaults to a value that’s half the length of the smallest edge. **roundCrop.ry (optional, number)** - Specifies the radius along the y-axis of the ellipse. If a value isn’t provided, the image handler defaults to a value that’s half the length of the smallest edge. **roundCrop.top(optional, number)** - Specifies the offset from the top of the original image to place the center of the ellipse. If a value isn’t provided, the image handler defaults to a value that’s half of the height. **roundCrop.left (optional, number)** - Specifies the offset from the left-most edge of the original image to place the center of the ellipse. If a value isn’t provided, the image handler defaults to a value that’s half of the width.

  ```
  const imageRequest = JSON.stringify({
      bucket: "<myImageBucket>",
      key: "<myImage.jpeg>",
      edits: {
          roundCrop: {
              rx: 30,   // x-axis radius
              ry: 20,   // y-axis radius
              top: 300, // offset from top edge of original image
              left: 500 // offset from left edge of original image
          }
      }
  })
  ```

**Note**  
 **roundCrop** is not supported for animated (such as, GIF) images.

# Overlay an image
<a name="overlay-an-image"></a>

This solution can overlay images on top of others, for cases like watermarking copyrighted image. To overlay an image, add the **overlayWith** property to the **edits** property in the [image request](use-the-solution-with-a-frontend-application.md).

 **overlayWith(optional, object)** - Overlays an image on top of the original. For example:

```
const imageRequest = JSON.stringify({
    bucket: "<myImageBucket>",
    key: "<myImage.jpeg>",
    edits: {
    overlayWith: {
        bucket: "<myImageBucket>",
        key: "<myOverlayImage.jpeg>",
        alpha: 0-100, // Opaque (0) to Transparent (100)
        wRatio: 0-100, // Ratio of the underlying image that the overlay width should be
        hRatio: 0-100, // Ratio of the underlying image that the overlay height should be
        options: {
                top: "-10p",
                left: 150
            }
        }
    }
})
```

The following **overlayWith** variables are shown in the previous code sample:
+  **overlayWith.bucket (required, string)** - Specifies the bucket that the overlay image should be retrieved from. This bucket must be present in the `SOURCE_BUCKETS` parameter.
+  **overlayWith.key (required, string)** - Specifies the object key that is used for the overlay image.
+  **overlayWith.alpha (optional, number)** - Specifies the opacity that should be used for the overlay image. This can be set from 0 (fully opaque) and 100 (fully transparent).
+  **overlayWith.wRatio (required, number)** - Specifies the percentage of the width of underlying image that the overlay image should be sized to. This can be set from 0 and 100, where 100 indicates that the overlay image has the same width as the underlying image.
+  **overlayWith.hRatio (required, number)** - Specifies the percentage of the height of underlying image that the overlay image should be sized to. This can be set from 0 and 100, where 100 indicates that the overlay image has the same height as the underlying image.
+  **overlayWith.options.top (optional, number \$1 string)** - Speciﬁes the distance in pixels from the top edge of the underlying photo that the overlay should be placed. A number formatted as a string with a `p` at the end is treated as a percentage.
+  **overlayWith.options.left (optional, number \$1 string)** - Speciﬁes the distance in pixels from the left edge of the underlying photo that the overlay should be placed. A number formatted as a string with a `p` at the end is treated as a percentage.

**Note**  
 **overlayWith** is not fully supported for animated (such as, GIF) images. Instead, only the first frame will receive an overlay.

# Overwrite animated status
<a name="overwrite-animated-status"></a>

This solution assumes that GIF files with multiple pages should be animated. If you’d like to indicate that a GIF should not be animated, or that another file type should be animated, include the animated property in the edits property in the image request.
+  **animated (optional, boolean)** - Overwrites the initial animated status of the image. If the value is `true` , the solution will attempt to process the image as animated. For example:

  ```
  const imageRequest = JSON.stringify({
      bucket: "<myImageBucket>",
      key: "<myImage.webp>",
      edits: {
          animated: true
      }
  })
  ```

  If it is `false`, the solution will process the image as a still image. For example:

  ```
  const imageRequest = JSON.stringify({
      bucket: "<myImageBucket>",
      key: "<myImage.gif>",
      edits: {
          animated: false
      }
  })
  ```

**Note**  
If an image does not have multiple pages, it will always be processed as still, regardless of the **edits.animated** property. The following filters are not supported for images that are animated: **rotate**, **smartCrop**, **roundCrop**, and **contentModeration**.

# Activate and use content moderation
<a name="activate-and-use-content-moderation"></a>

This solution can detect inappropriate content using Amazon Rekognition. To activate content moderation, add the **contentModeration** property to the **edits** property in the [image request](use-the-solution-with-a-frontend-application.md).
+  **contentModeration (optional, boolean \$1\$1 object)** - Activates the content moderation feature for an original image. If the value is true, then the feature detects inappropriate content using Amazon Rekognition with a minimum confidence that’s set higher than 75%. If Amazon Rekognition finds inappropriate content, the solution blurs the image. For example:

  ```
  const imageRequest = JSON.stringify({
      bucket: "<myImageBucket>",
      key: "<myImage.jpeg>",
      edits: {
          contentModeration: true
      }
  })
  ```

  The following **contentModeration** variables are shown in the following code sample:
+  **contentModeration.minConfidence (optional, number)** - Specifies the minimum confidence level for Amazon Rekognition to use. Amazon Rekognition only returns detected content that’s higher than the minimum confidence. If a value isn’t provided, the default value is set to 75%.
+  **contentModeration.blur (optional, number**) - Specifies the intensity level that an image is blurred if inappropriate content is found. The number represents the sigma of the Gaussian mask, where *sigma = 1 \$1 radius /2*. For more information, refer to the [sharp](https://sharp.pixelplumbing.com/api-operation#blur) documentation. If a value isn’t provided, the default value is set to 50.
+  **contentModeration.moderationLabels (optional, array)** - Identifies the specific content to search for. The image is blurred only if Amazon Rekognition locates the content specified in the **smartCrop.moderationLabels** provided. You can use either a top-level category or a second-level category. Top-level categories include its associated second-level categories. For more information about moderation label options, refer to [Content moderation](https://docs.aws.amazon.com/rekognition/latest/dg/moderation.html) in the *Amazon Rekognition Developer Guide*.

  ```
  const imageRequest = JSON.stringify({
      bucket: "<myImageBucket>",
      key: "<myImage.jpeg>",
      edits: {
          contentModeration: {
              minConfidence: 90,  // minimum confidence level for inappropriate content
              blur: 80,           // amount to blur image
              moderationLabels: [ // labels to search for
                 "Hate Symbols",
                 "Smoking"
               ]
          }
      }
  })
  ```

**Note**  
 **contentModeration** is not supported for animated (such as, GIF) images.

# Include custom response headers
<a name="include-custom-response-headers"></a>

This solution allows you to include headers you’d like returned alongside the response, as part of your request.
+  **headers (optional, object)** - Includes the provided headers in the response. Header should be written in Pascal-Case and cannot overwrite headers that would otherwise be present in the response (Except for Cache-Control).

  ```
  const imageRequest = JSON.stringify({
      bucket: "<myImageBucket>",
      key: "<myImage.jpeg>",
      headers: {
          "Cache-Control":"max-age=86400,public"
          "Custom-Header":"some-custom-value"
      }
  })
  ```

**Note**  
A deny-list is maintained which restricts which headers can be included with this feature. Headers which may serve a purpose for the browser or are used to support authentication/authorization are included in this deny-list. For an exact list of the regular expressions which are restricted, visit [constants.ts](https://github.com/aws-solutions/serverless-image-handler/blob/main/source/image-handler/lib/constants.ts) on the Solution GitHub repository.  
The presence of the `expires` query parameter will cause the Cache-Control header to be overridden, regardless of any value provided in the headers field.  
If your deployment is using the S3 Object Lambda Architecture , the headers at [this link](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/edge-function-restrictions-all.html#function-restrictions-disallowed-headers) cannot be included as custom headers.

# Include request expiration
<a name="include-request-expiration"></a>

This solution supports the expires query parameter, which is used to decide whether the Lambda should process a request. If an Expiry date is in the future, the request will be processed. If the Expiry date has already passed, the Lambda will return a 400 Bad Request error with the code: ImageRequestExpired.

Values in the expires query parameter are expected in the format: YYYYMMDDTHHmmssZ. For example: April 9th, 2024, at 10:30:15 UTC -4 would become 20240409T143015Z.

Request expiry is compatible with signatures, and should be included as part of the path when signing a request. For example:

```
const secret = '<YOUR_SECRET_VALUE_IN_SECRETS_MANAGER>';
const path = '/<YOUR_PATH>'; // Add the first '/' to path.
const expires = 'expires=<YOUR_EXPIRY>';
const to_sign = `${path}?${expires}`
const signature = crypto.createHmac('sha256', secret).update(to_sign).digest('hex');
```

**Note**  
If the expires query parameter is being used in conjunction with any query parameter based edits. When generating a signature, please ensure that the query parameters are sorted. For more information, see .

# Use supported Query Parameter edits
<a name="use-supported-query-param-edits"></a>

The solution supports the definition of certain image edits through the use of query parameters. These query parameters can be used by themselves, or in conjunction with base64 or Thumbor-style edits. For example

```
https://<ApiEndpoint>/<image.jpeg>?format=<FormatType>
```

```
https://<ApiEndpoint>/<base64EncodedRequest>?format=<FormatType>
```

```
https://<ApiEndpoint>/<modification>/<image.jpeg>?format=<FormatType>
```

If a query parameter includes an edit already included in the request, it will overwrite the included value.

The following query parameter edits are currently available:


| Query parameter name | Description | Options | Default | 
| --- | --- | --- | --- | 
|   ** [format](https://sharp.pixelplumbing.com/api-output#toformat) **   |  Sets the output to the provided format  |  jpg, jpeg, heic, png, raw, tiff, webp, gif, avif  |  None  | 
|   ** [fit](https://sharp.pixelplumbing.com/api-resize#resize) **   |  The method that should be used when resizing  |  cover, contain, fill, inside, outside  |  cover  | 
|   ** [width](https://sharp.pixelplumbing.com/api-resize#resize) **   |  The width in pixels, the image should be resized to.  |  Positive integer or 0  |  None  | 
|   ** [height](https://sharp.pixelplumbing.com/api-resize#resize) **   |  The height in pixels, the image should be resized to.  |  Positive integer or 0  |  None  | 
|   ** [rotate](https://sharp.pixelplumbing.com/api-operation#rotate) **   |  The number of degrees the image should be rotated  |  0-359 or blank (for null)  |  None  | 
|   ** [flip](https://sharp.pixelplumbing.com/api-operation#flip) **   |  Mirror the image vertically  |  True/False  |  False  | 
|   ** [flop](https://sharp.pixelplumbing.com/api-operation#flop) **   |  Mirror the image horizontally  |  True/False  |  False  | 
|   ** [greyscale](https://sharp.pixelplumbing.com/api-colour#greyscale) **   |  Convert to 8-bit greyscale  |  True/False  |  False  | 

# Use supported Thumbor filters
<a name="use-supported-thumbor-filters"></a>

This solution supports the Thumbor filters listed in this section, using API calls. To retrieve your API endpoint for the solution, refer [Use the solution with a frontend application](use-the-solution-with-a-frontend-application.md) for instructions.

To use the filters, use the following example syntax for the API call:

```
https://<ApiEndpoint>/<modification>/<image.jpeg>
```

# Define the source bucket for the request
<a name="define-the-source-bucket"></a>

To define the bucket used when getting the image for a request, include **s3:BucketName** as a modification in your request. For example, if your source buckets were `"test-bucket-1, the-other-test-bucket"`, to indicate that `the-other-test-bucket` should be used when processing an image, enter the following:

```
https://<ApiEndpoint>/s3:the-other-test-bucket/<image.jpeg>
```

**Note**  
Using the **s3:BucketName** tag requires that the bucket chosen is part of the **SourceBuckets** provided upon deployment. For information on how to change the **SourceBuckets** after deployment, see [Backward compatibility](backward-compatibility.md).

# Resize an image
<a name="resize-an-image"></a>

To resize an image, specify `fit-in` and the desired image size. For example, to resize a JPEG image to 300 pixels wide and 400 pixels tall, enter the following:

```
https://<ApiEndpoint>/fit-in/<300x400>/<image.jpeg>
```

# Use filters
<a name="use-filters"></a>

To use filters, specify a filter from the following table. For example, to blur a JPEG image, enter the following:

```
https://<ApiEndpoint>/filters:blur(7)/<image.jpeg>
```

**Note**  
Some Thumbor filters aren’t supported in the current version of this solution. This might affect legacy users with advanced image request configurations. For notes about Thumbor compatibility and source image storage limitations, see [Backward compatibility](backward-compatibility.md). For examples of filter usage, refer to the [Thumbor documentation](http://thumbor.readthedocs.io/en/latest/filters.html).


| Filter name | Filter syntax | 
| --- | --- | 
|   **Animated**   |   `/filters:animated(true/false)/`   | 
|   **Autojpg**   |   `/filters:autojpg()/`   | 
|   **Background color**   |   `/filters:background_color(color)/`   | 
|   **Blur**   |   `/filters:blur(7)/`   | 
|   **Color fill**   |   `/filters:fill(color)/`   | 
|   **Convolution**   |   `/filters:convolution(1;2;1;2;4;2;1;2;1,3,false)/`   | 
|   **Crop**   |   `/10x10:100x100/`   | 
|   **Equalize**   |   `/filters:equalize()/`   | 
|   **Grayscale**   |   `/filters:grayscale()/`   | 
|   **Image format** `(.gif, .jpeg, .png, .avif, .webp, .tiff, .raw, .heif)`   |   `/filters:format(image_format)`   | 
|   **No upscale**   |   `/filters:no_upscale()/`   | 
|   **Proportion**   |   `/filters:proportion(0.0-1.0)/`,  | 
|   ** Quality**   |   `/filters:quality(0-100)/`   | 
|   **Resize**   |   `/fit-in/800x1000/`   | 
|   **RGB**   |   `/filters:rgb(20,-20,40)/`   | 
|   **Rotate**   |   `/filters:rotate(90)/`   | 
|   **Sharpen**   |   `/filters:sharpen(0.0-10.0, 0.0-2.0, true/false)/`   | 
|   **Smart Crop**   |   `/filters:smart_crop(faceIndex, facePadding)/`   | 
|   **Stretch**   |   `/filters:stretch()/`   | 
|   **Strip Exif**   |   `/filters:strip_exif()/`   | 
|   **Strip ICC**   |   `/filters:strip_icc()/`   | 
|   **Upscale**   |   `/filters:upscale()/`   | 
|   **Watermark**   |   `/filters:watermark(bucket,key,x,y,alpha[,w_ratio[,h_ratio]])`   | 

# Use multiple filters
<a name="use-multiple-filters"></a>

To use multiple filters on an image, list them in the same section of the URL. Filters process the image in the order that you specify them. For example:

```
https://<api-endpoint>/fit-in/<300x400>/filters:<fill>(<00ff00>)/filters:<rotate>(<90>)/<image.jpeg>
```

# Custom image requests
<a name="custom-image-requests"></a>

**Note**  
As of recent releases of Dynamic Image Transformation for Amazon CloudFront, editing the environment variables directly is not supported for the AutoWebP (v7.0.0), SourceBuckets (v6.2.6), OriginShieldRegion(v7.0.0) and EnableS3ObjectLambda(v7.0.0) template parameters, instead, follow the instructions in [Updating template parameters](updating-template-parameters.md).

You can customize most settings for this solution by editing and updating the environment variables associated with the image handler Lambda function. You can find the image handler function in the AWS Management Console using one of the following methods:

 **Using the AWS Lambda console:** 

1. Sign in to the [AWS Lambda console](https://console.aws.amazon.com/lambda).

1. Select **Functions**. The image handler function is listed with the following naming convention: `[.replaceable]<StackName>`-ImageHandlerFunction-`[.replaceable]<UniqueID>`.

 **Using the AWS CloudFormation console:** 

1. Sign in to the [AWS CloudFormation console](https://console.aws.amazon.com/cloudformation).

1. On the **Stacks** page, select this solution’s installation stack.

1. Choose the **Resources** tab. The image handler function is listed with a **Logical ID** of `ImageHandlerFunction`.

After opening the Lambda function, go to the **Environment variables** section. Use the following key-value pairs to customize the solutions settings.

 **Note:** The solution uses the  to determine these initial key values, except for **REWRITE\$1MATCH\$1PATTERN** and **REWRITE\$1SUBSTITUTION**.


| Variable Key | Value Type | Description | 
| --- | --- | --- | 
|   **AUTO\$1WEPB**   |   `Yes/No`   |  Choose whether to automatically accept webp image formats.  | 
|   **CORS\$1ENABLED**   |   `Yes/No`   |  Indicates whether to return an **Access-Control-Allow-Origin** header with the image handler API response.  | 
|   **CORS\$1ORIGIN**   |   `String`   |  This value is returned by the API in the **Access-Control-Allow-Origin** header. An asterisk value supports any origin. We recommend specifying a specific origin (Ex: `https://example.domain`) to restrict cross-site access to your API.  **Note:** This value is ignored if **CORS\$1ENABLED** is set to `No`.  | 
|   **ENABLE\$1DEFAULT\$1FALLBACK\$1IMAGE**   |   `Yes/No`   |  Choose whether to return the default fallback image when errors occur.  | 
|   **DEFAULT\$1FALLBACK\$1IMAGE\$1BUCKET**   |   `String`   |  Specifies the S3 bucket which contains the default fallback image.  **Note:** This value is ignored if the **ENABLE\$1DEFAULT\$1FALLBACK\$1IMAGE** parameter is set to `No`.  | 
|   **DEFAULT\$1FALLBACK\$1IMAGE\$1KEY**   |   `String`   |  Defines the default fallback image S3 object key, including the prefix.  **Note:** This value is ignored if the **ENABLE\$1DEFAULT\$1FALLBACK\$1IMAGE** parameter is set to `No`.  | 
|   **ENABLE\$1SIGNATURE**   |   `Yes/No`   |  Choose whether to use the image URL signature.  | 
|   **REWRITE\$1MATCH\$1PATTERN**   |   `Regex`   |  By default, this parameter is empty. If you overwrite this default value, use a JavaScript-compatible regular expression for matching custom image requests using the rewrite function. This value should match the JavaScript compatible regular expression. For example, `/(filters-)/gm`.  | 
|   **REWRITE\$1SUBSTITUTION**   |   `String`   |  By default, this parameter is empty. If you overwrite this default value, use a substitution string for custom image requests using the rewrite function. For example, `filters:`.  | 
|   **SECRETS\$1MANAGER**   |   `String`   |  Defines the Secrets Manager secret that contains the secret key for the image URL signature.  **Note:** This value is ignored if `[.replaceable]ENABLE_SIGNATURE` is set to `No`.  | 
|   **SECRET\$1KEY**   |   `String`   |  Defines the Secrets Manager secret key that contains the secret value to create the image URL signature.  **Note:** This value is ignored if **ENABLE\$1SIGNATURE** is set to `No`.  | 
|   **SOURCE\$1BUCKETS**   |   `String`   |  The S3 bucket (or buckets) in your account that contain(s) the original images. If you’re providing multiple buckets, separate them by commas.  | 

# Updating template parameters
<a name="updating-template-parameters"></a>

Use the following instructions to update template parameters in such a way that there is no drift between your resources and your CloudFormation stack, and to ensure that all necessary changes are made to your resources:

1. Sign in to the AWS CloudFormation console, select your existing Dynamic Image Transformation for Amazon CloudFront CloudFormation stack, and select Update.

1. Leaving Use Current Template selected, click Next

1. Modify the template parameters as needed

1. Continue through the rest of the workflow as you would when creating the stack.

**Note**  
Modifications made to SIH which are not reflected in the CloudFormation template may be removed when updating template parameters in this fashion

# Use the rewrite feature
<a name="use-the-rewrite-feature"></a>

You can use this solution’s rewrite feature to migrate your current image request model to the Dynamic Image Transformation for Amazon CloudFront solution, without changing the applications to accommodate new image URLs.

The rewrite feature translates custom URL image requests into Thumbor-consumable formats, based on JavaScript-compatible regular expression match patterns and substitution strings. After the image request is converted into Thumbor-consumable form, it’s then processed as a Thumbor image request and edits are mapped to the new sharp image library.

This feature requires that you populate the following environment variables in the [image handler function](custom-image-requests.md). These environment variables are added to the function by default, but are left empty for user input if the rewrite feature is needed.


| Variable Key | Value Type | Description | 
| --- | --- | --- | 
|   **REWRITE\$1MATCH\$1PATTERN**   |   `Regex`   |  By default, this parameter is empty. If you overwrite this default value, use a JavaScript-compatible regular expression for matching custom image requests using the rewrite function. This value should match the JavaScript compatible regular expression. For example, `/(filters-)/gm`.  | 
|   **REWRITE\$1SUBSTITUTION**   |   `String`   |  By default, this parameter is empty. If you overwrite this default value, use a substitution string for custom image requests using the rewrite function. For example, `filters:`.  | 

You can use any of the Thumbor-supported filters listed in this section with the rewrite feature. The following sections provide examples.

# Replace filters- with filters:
<a name="example-1-replace-filters"></a>

If you put `/(filters-)/gm in REWRITE_MATCH_PATTERN` and `filters:` in **REWRITE\$1SUBSTITUTION**, you can call

```
https://<your-CloudFront-distribution>/filters:rotate(90)/<your-image>
```

instead of

```
https://<your-CloudFront-distribution>/filters-rotate(90)/<your-image>
```

to rotate your image. In this example, the solution replaces `filters-` (filters hyphen syntax) with `filters:` (filters colon syntax).

# Reverse path order
<a name="example-2-reverse-path-order"></a>

You can place filters at the end of the path rather than before the image key.

1. Use the `REWRITE_MATCH_PATTERN` with a regular expression that parses the path into two groups. The solution then uses `REWRITE_SUBSTITUTION` to switch the order of the groups.

1. Use a regular expression specified by `REWRITE_MATCH_PATTERN` to parse the path into groups for a request like `https://abcd.cloudfront.net/imagekey.png/fit-in/200x200`, where the image key appears before the filters. For example:

   ```
   REWRITE_MATCH_PATTERN = /^\/(.*?\..*?)\/(.+)$/gm
   ```

1. Reverse the order of the fields with `REWRITE_SUBSTITUTION` to convert the request into a Thumbor style request like `https://abcd.cloudfront.net/fit-in/200x200/imagekey.png`, where the image key is moved to the end of the request. For example:

   ```
   REWRITE_SUBSTITUTION = /$2/$1
   ```

# Parse request type
<a name="example-3-parse-request-type"></a>

Refer to [image-request.spec.js.file](https://raw.githubusercontent.com/aws-solutions/serverless-image-handler/develop/source/image-handler/test/image-request/parse-request-type.spec.ts), in the Dynamic Image Transformation for Amazon CloudFront GitHub repository.

# Rotate images manually
<a name="rotate-images-manually"></a>

Not all browsers support rotational EXIF data for all image formats and you may notice visual issues when viewing your images through the browser. This tends to be more common with the WebP image format. Sharp allows the passing of a null value in the rotate field to indicate that the orientation associated with the EXIF orientation tag should be manually applied (and the tag removed). You can implement this for base64 image requests through the inclusion of a `rotate: null` edit, or for Thumbor-style requests by including `filters:strip_exif()` or `filters:strip_icc()` in your request path.

# ECS architecture
<a name="ecs-architecture-usage"></a>

This section covers how to use the solution when deployed with the ECS architecture.

# Deploy the template
<a name="deploy-the-template"></a>

Deploy the ECS CloudFormation template following the instructions in [Deploy ECS architecture](deploy-alb-ecs.md). Thee deployment will provision all necessary resources including the Admin UI, Application Load Balancer, ECS service, and CloudFront distribution.

# Access the Admin UI
<a name="access-the-admin-ui"></a>

After deployment completes:

1. Navigate to the CloudFormation stack outputs in the AWS console.

1. Find the `AdminUIUrl` output value and open the link.

1. Sign in using the Cognito credentials sent to the admin email address specified during deployment.

# Create origins
<a name="create-origins"></a>

Origins define the source locations for your images.

1. In the Admin UI, navigate to the Origins section.

1. Click **Create Origin** and provide:
   +  **Origin Name**: Descriptive name for the origin
   +  **Origin Domain**: Domain name (e.g., `my-bucket.s3.amazonaws.com`)
   +  **Origin Path**: Optional path prefix (e.g., `/images`)
   +  **Origin Headers**: Optional custom headers

1. Click **Save** to create the origin.

# Create transformation policies
<a name="create-transformation-policies"></a>

Transformation policies define how images are processed based on conditions. You can create policies using either the Admin UI interface or by providing JSON configuration directly.

## Using the Admin UI
<a name="using-the-admin-ui"></a>

1. In the Admin UI, navigate to the **Policies** section.

1. Click **Create Policy** and provide:
   +  **Policy Name**: Descriptive name for the policy
   +  **Description**: Optional description of what the policy does

1. Configure transformations using the UI:
   + Click **Add Transformation** to add image processing operations
   + Select transformation type (resize, format, quality, etc.)
   + Configure transformation parameters using the form fields

1. Configure outputs using the UI:
   + Click **Add Output** to define output specifications
   + Select output type (quality, format, autosize)
   + Configure output parameters using the form fields

1. Click **Save** to create the policy.

## Using Management API
<a name="using-management-api"></a>

Alternatively, you can create policies by providing JSON configuration directly using the Management API:

1. In the Admin UI, navigate to the **Policies** section.

1. Click **Create Policy** and provide:
   +  **Policy Name**: Descriptive name for the policy
   +  **Description**: Optional description
   +  **Policy JSON**: JSON configuration defining transformations and outputs

1. Example policy JSON:

   ```
   {
       "outputs": [
           {
               "type" : "quality",
               "value" : [
                   77,
                   [0,1,50],
                   [1,2,75],
                   [2,500,90]
               ]
           },
           {
               "type": "format",
               "value": "auto"
           },
           {
               "type": "autosize",
               "value": [320,480,640,960,1440,1920]
           }
       ],
       "transformations": [
           {
               "transformation": "blur",
               "value": 7
           },
           {
               "transformation": "convolve",
               "value": {
                   "width": 3,
                   "height": 3,
                   "kernel": [1,0,-1,0,0,0,-1,0,1]
               }
           },
           {
               "transformation": "extract",
               "value": [10,10,100,100]
           },
   
           {
               "transformation": "flip",
               "value": true,
               "condition" : {
                   "field": "header.XYZ",
                   "value": "ABC"
               }
           },
           {
               "transformation": "flop",
               "value": false
           },
           {
               "transformation": "grayscale",
               "value": true
           },
           {
               "transformation": "normalize",
               "value": true
           },
           {
               "transformation": "resize",
               "value": {
                   "width": 400,
                   "height": 600,
                   "fit": "contain"
               }
           },
           {
               "transformation": "rotate",
               "value": 60,
               "condition" : {
                   "field": "header.XYZ",
                   "value": "ABC"
               }
           },
           {
               "transformation": "sharpen",
               "value": {
                   "sigma": 5
               }
           },
           {
               "transformation": "smartCrop",
               "value": true
           },
   
           {
               "transformation": "stripExif",
               "value": true
           },
           {
               "transformation": "stripIcc",
               "value": true
           },
           {
               "transformation": "tint",
               "value": "blue"
           },
           {
               "transformation": "watermark",
               "value": [ "watermarkURL", [15, 15, 0.1, 0.4, 0.4]]
           }
       ]
   }
   ```

1. Click **Save** to create the policy.

# Transformation filter reference
<a name="transformation-filter-reference"></a>

The ECS architecture supports a comprehensive set of image transformation filters using a simplified syntax. The following table provides the complete filter reference:


| Filter Name | Filter Syntax | Notes | 
| --- | --- | --- | 
|   **Blur**   |   `blur=30`   |  Integer from 0.3 to 1000  | 
|   **Convolve**   |   `convolve.width=3` `convolve.height=3` `convolve.kernel=[1,0,-1,0,0,0,-1,0,1]`   |  Requires 3 parameters: width, height, kernel  | 
|   **Extract**   |   `extract=[10,10,200,200]`   |  Array of 4 non-negative integers  | 
|   **Flatten**   |   `flatten=aliceblue` `flatten=[0,0,255,1]`   |  Accepts color names or RGBA tuples  | 
|   **Flip**   |   `flip=true`   |  Boolean  | 
|   **Flop**   |   `flop=true`   |  Boolean  | 
|   **Format**   |   `format=webp`   |  Accepts: jpg, jpeg, png, tiff, webp, gif, avif  | 
|   **Greyscale**   |   `greyscale=true`   |  Boolean  | 
|   **Normalize**   |   `normalize=true`   |  Boolean  | 
|   **Quality**   |   `quality=0.5`   |  Integer from 0 to 1  | 
|   **Resize**   |   `resize.width=200` `resize.ratio=0.5` `resize.fit=contain` `resize.withoutEnlargement=true` `resize.background=blue`   |  Must specify: height, width, or ratio  | 
|   **Rotate**   |   `rotate=90`   |  Integer  | 
|   **Sharpen**   |   `sharpen=true` or `sharpen=sigma=5` `sharpen.m1=2` `sharpen.m2=1` `sharpen.x1=2` `sharpen.y2=20` `sharpen.y3=20`   |  Accepts either boolean to perform a fast mild sharpen, or additional parameters for a slower but more accurate sharpen. See [Sharp docs](https://sharp.pixelplumbing.com/api-operation/#sharpen) for more information.  | 
|   **Smart Crop**   |   `smartCrop=true` or `smartCrop.index=1` `smartCrop.padding=200`   |  Accepts either boolean or index \$1 padding parameters. Boolean will automatically perform cropping.  | 
|   **Strip ICC**   |   `stripIcc=true`   |  Strip ICC and enforces sRGB color space  | 
|   **Strip EXIF**   |   `stripExif=true`   |  Removes image metadata  | 
|   **Tint**   |   `tint=aliceblue` `tint=[0,0,255,1]`   |  Accepts color names or RGBA tuples  | 
|   **Watermark**   |   `watermark=https://example.com/overlayImage.png,[15,15,0.1,0.4,0.4]`   |  Expects a format of: [watermarkURL, [x, y, alpha, widthRatio, heightRatio]] Note: For security reasons, the origin the watermark image is hosted at must be configured as an origin within DIT.  | 

# Create mappings
<a name="create-mappings"></a>

Mappings connect URL patterns to specific origins and transformation policies.

1. In the Admin UI, navigate to the Mappings section.

1. Click **Create Mapping** and provide:
   +  **Mapping Name**: Descriptive name for the mapping
   +  **Mapping Type**: Choose `PATH_MAPPING` or `HOST_HEADER_MAPPING` 
   +  **Pattern**: URL pattern (e.g., `/mobile/*` or `example.com`)
   +  **Origin**: Select the origin created earlier
   +  **Policy**: Select the transformation policy (optional)
   +  **Priority**: Numeric priority for mapping evaluation

1. Click **Save** to create the mapping.

# Test image transformations
<a name="test-image-transformations"></a>

Test your configuration using the CloudFront distribution created by the solution.

1. Get the CloudFront distribution URL from the CloudFormation stack outputs (`CloudFrontDistributionDomainName`).

1. Access images using the pattern defined in your mappings:

   ```
   https://<cloudfront-domain>/<mapping-pattern>/<image-path>
   ```

1. Example:

   ```
   https://d1234567890.cloudfront.net/mobile/sample-image.jpg
   ```

1. The image will be processed according to the transformation policy associated with the matching mapping.

# Monitor and manage
<a name="monitor-and-manage"></a>

Use the Admin UI to:
+ View and edit existing origins, policies, and mappings
+ Update configurations as needed

The ECS architecture provides a scalable, containerized solution for advanced image processing with full administrative control through the web-based Admin UI.