---
name: azion-sign-origin-requests-with-hmac
description: >-
  Serve a private S3-compatible bucket through a connector by signing each origin request with HMAC credentials.
---

# Sign origin requests with HMAC

You can serve the objects of a private bucket through a [connector](/en/documentation/platform/connectors/) that signs every request to the bucket's S3-compatible endpoint, from Azion Console, the Azion CLI, or the API. The signing is HMAC, a setting of [Origin Shield](/en/documentation/platform/connectors/#origin-shield) on a connector of type `http`. To accept only Azion's addresses at your origin instead, refer to [Allow Azion's IP ranges at your origin](/en/documentation/support/retrieve-azion-ip-ranges/).

The examples use the Azion Object Storage endpoint `s3.us-east-005.azionstorage.net` with the region `us-east-005`, and an object named `hello.txt` at the root of the bucket. Replace them with the endpoint, the region, and an object of your provider.

---

Select the interface you use. The prerequisites and every task on this page follow that choice.

## Prerequisites

- An application served by a workload, and the workload domain or a domain of your own that the workload answers on. To create them, refer to [Applications quickstart](/en/documentation/platform/applications/quickstart/).
- A private bucket on an S3-compatible endpoint, and an access key and a secret key that can read the bucket. For the credentials of Azion Object Storage, refer to [S3 compatibility](/en/documentation/platform/object-storage/s3-compatibility/).
- `curl`, to verify the result.

**Console**

- Access to [Azion Console](https://console.azion.com/).

**CLI**

- The [Azion CLI](/en/documentation/devtools/cli/), installed and authorized.
- The ID of your application.

**API**

- A personal token and the ID of your application.

---

## Create the signing connector

The connector reaches the endpoint over HTTPS and sends the endpoint's own name as the `Host` header. Its path, `/<bucket-name>`, goes in front of every request path, so a request for `/hello.txt` reaches the endpoint as `/<bucket-name>/hello.txt`. With Origin Shield on, HMAC signs each of these requests with your credentials, using the `aws4_hmac_sha256` type.

**Console**

To create the connector in Azion Console:

1. **Open the Connectors page**

   Access [Azion Console](https://console.azion.com/) > **Connectors**.

2. **Start a new connector**

   The **Create Connector** page opens.

3. **Name the connector**

   In **General**, enter `my-bucket-connector` as the **Name**.

4. **Select the HTTP type**

   In **Connector Type**, select *HTTP*.

5. **Enter the endpoint as the address**

   Under **Address Management**, enter `s3.us-east-005.azionstorage.net` in **Address**, without a protocol or a port.

6. **Send the endpoint's name in the Host header**

   In **Host**, enter `s3.us-east-005.azionstorage.net`.

7. **Enter the bucket as the path**

   In **Path**, enter `/` followed by the name of your bucket.

8. **Connect over HTTPS only**

   In **Transport Protocol Policy**, select *Force HTTPS*.

9. **Turn on Origin Shield**

   In **Modules**, turn on **Origin Shield**.

10. **Turn on HMAC**

    Turn on the switch of the **HMAC** section. **Type** shows `aws4_hmac_sha256` and cannot change.

11. **Enter the region and the service**

    In **Region**, enter `us-east-005`. In **Service**, enter `s3`.

12. **Enter the credentials**

    In **Access Key** and **Secret Key**, enter the credentials that can read the bucket.

13. **Select Create**

The connector exists in your account and signs each request it sends to the endpoint.

**CLI**

The CLI creates a connector from a JSON file. Save this body as `connector.json`, and replace `<bucket-name>`, `<access-key>`, and `<secret-key>` with your values:

```json
{
  "name": "my-bucket-connector",
  "type": "http",
  "attributes": {
    "addresses": [{ "address": "s3.us-east-005.azionstorage.net" }],
    "connection_options": {
      "transport_policy": "force_https",
      "host": "s3.us-east-005.azionstorage.net",
      "path_prefix": "/<bucket-name>"
    },
    "modules": {
      "origin_shield": {
        "enabled": true,
        "config": {
          "origin_ip_acl": { "enabled": false },
          "hmac": {
            "enabled": true,
            "config": {
              "type": "aws4_hmac_sha256",
              "attributes": {
                "region": "us-east-005",
                "service": "s3",
                "access_key": "<access-key>",
                "secret_key": "<secret-key>"
              }
            }
          }
        }
      }
    }
  }
}
```

The file holds your secret key. Delete it after you create the connector.

To create the connector with the Azion CLI, pass the file and the type. The command needs `--type` even though the file names the type:

```bash
azion create connector --type http --file connector.json
```

The output carries the ID of the new connector:

```text
Created Connector with ID <connector-id>
```

Record the ID: the next task passes it as `<connector-id>`. The connector exists in your account and signs each request it sends to the endpoint.

**API**

The API takes the connector as a JSON body. Save this body as `connector.json`, and replace `<bucket-name>`, `<access-key>`, and `<secret-key>` with your values:

```json
{
  "name": "my-bucket-connector",
  "type": "http",
  "attributes": {
    "addresses": [{ "address": "s3.us-east-005.azionstorage.net" }],
    "connection_options": {
      "transport_policy": "force_https",
      "host": "s3.us-east-005.azionstorage.net",
      "path_prefix": "/<bucket-name>"
    },
    "modules": {
      "origin_shield": {
        "enabled": true,
        "config": {
          "origin_ip_acl": { "enabled": false },
          "hmac": {
            "enabled": true,
            "config": {
              "type": "aws4_hmac_sha256",
              "attributes": {
                "region": "us-east-005",
                "service": "s3",
                "access_key": "<access-key>",
                "secret_key": "<secret-key>"
              }
            }
          }
        }
      }
    }
  }
}
```

The file holds your secret key. Delete it after you create the connector.

To create the connector with the API, send a `POST` request to the connectors endpoint. Replace `[TOKEN VALUE]` with your personal token:

```bash
curl --request POST \
  --url https://api.azion.com/v4/workspace/connectors \
  --header 'Accept: application/json' \
  --header 'Authorization: Token [TOKEN VALUE]' \
  --header 'Content-Type: application/json' \
  --data @connector.json
```

The API answers `202` with `"state": "pending"` and the stored connector, whose `id` the next task passes as `<connector-id>`. In the response, `attributes.modules.origin_shield.config.hmac.enabled` reads `true`. With `hmac.enabled` as `true` and no `hmac.config`, the API refuses the request with `400` and `28014` `Module configuration must be provided when 'enabled' is true.`

Turning HMAC off later removes the stored credentials. To turn it back on, enter the access key and the secret key again.

---

## Send requests to the connector

A connector receives no traffic until a rule on your application names it. This rule matches requests for `/hello.txt` in the request phase, and its *Set Connector* behavior sends them to the signing connector. Replace `/hello.txt` with the path of your object. If your application already has a rule that matches every path, keep this rule after it, so this rule decides the connector for the object. For more information, refer to [Set Connector](/en/documentation/platform/applications/rules-engine/#set-connector).

**Console**

To create the rule in Azion Console:

1. **Open the application**

   Access [Azion Console](https://console.azion.com/) > **Applications**, and select your application.

2. **Select the Rules Engine tab**

3. **Select + Rule**

4. **Name the rule**

   In **General**, enter `send-object-to-bucket` as the **Name**.

5. **Select the request phase**

   In **Phase**, select *Request Phase*.

6. **Set the criterion**

   Under **Criteria**, select the variable `${uri}` and the operator `starts_with`, and enter `/hello.txt` as the argument.

7. **Select the Set Connector behavior**

   Under **Behaviors**, select *Set Connector*.

8. **Select the signing connector**

   In **Connector**, select `my-bucket-connector`.

9. **Select Save**

The rule appears in the **Rules Engine** tab, under the **Request** heading.

**CLI**

Keep the rule in a file: on a command line, the shell would expand `${uri}`. Save this body as `rule.json`, and replace `<connector-id>` with the ID of the signing connector:

```json
{
  "name": "send-object-to-bucket",
  "active": true,
  "criteria": [
    [
      {
        "variable": "${uri}",
        "conditional": "if",
        "operator": "starts_with",
        "argument": "/hello.txt"
      }
    ]
  ],
  "behaviors": [
    {
      "type": "set_connector",
      "attributes": { "value": <connector-id> }
    }
  ]
}
```

To create the rule with the Azion CLI, run this command. Replace `<application-id>` with the ID of your application:

```bash
azion create rules-engine --application-id <application-id> --phase request --file rule.json
```

The output carries the ID of the new rule:

```text
Created Rules Engine with ID <rule-id>
```

The rule is active on your application, and it sends requests for `/hello.txt` to the signing connector.

**API**

Keep the request body in a file: on a command line, the shell would expand `${uri}`. Save this body as `rule.json`, and replace `<connector-id>` with the ID of the signing connector:

```json
{
  "name": "send-object-to-bucket",
  "active": true,
  "criteria": [
    [
      {
        "variable": "${uri}",
        "conditional": "if",
        "operator": "starts_with",
        "argument": "/hello.txt"
      }
    ]
  ],
  "behaviors": [
    {
      "type": "set_connector",
      "attributes": { "value": <connector-id> }
    }
  ]
}
```

To create the rule with the API, send a `POST` request to the request-phase rules of your application. Replace `<application-id>` with the ID of your application:

```bash
curl --request POST \
  --url https://api.azion.com/v4/workspace/applications/<application-id>/request_rules \
  --header 'Accept: application/json' \
  --header 'Authorization: Token [TOKEN VALUE]' \
  --header 'Content-Type: application/json' \
  --data @rule.json
```

The API answers `202` with `"state": "pending"`, and the response returns the stored rule with its `id` and its `order`. The rule is active on your application, and it sends requests for `/hello.txt` to the signing connector.

---

## Verify the signed request

The check is the same whichever interface you used. A new connector and a new rule take several minutes to reach Azion's distributed infrastructure, and data centers apply them at different times. Until then, a request can still reach the connector of another rule. Repeat the request until the endpoint answers. For more information, refer to [Propagation](/en/documentation/platform/connectors/how-it-works/#propagation).

Request the object through your workload. Replace `<workload-domain>` with the workload domain or your own domain:

```bash
curl -s -D - https://<workload-domain>/hello.txt
```

The endpoint answers `200` with the content of the object. These headers identify the storage endpoint as the source of the answer:

```text
HTTP/2 200
…
content-type: text/plain
…
server: azion webserver
x-amz-request-id: …
…
```

To compare, request the same object from the endpoint directly, without a signature:

```bash
curl -s -D - https://s3.us-east-005.azionstorage.net/<bucket-name>/hello.txt
```

The endpoint refuses the unsigned request with `401`, not `403`:

```text
HTTP/1.1 401
Server: azion webserver
…
<Error>
    <Code>UnauthorizedAccess</Code>
    <Message>bucket is not authorized: <bucket-name></Message>
</Error>
```

The workload returns the same `401` when HMAC is off on the connector. The connector signs each request to the private bucket, and your workload serves the object. For the fixes, refer to [The storage endpoint returns 401 UnauthorizedAccess](/en/documentation/platform/connectors/troubleshooting/#the-storage-endpoint-returns-401-unauthorizedaccess).

---

## Next steps

- [Origin IP ACL and HMAC](/en/documentation/platform/connectors/origin-shield/origin-ip-acl-and-hmac.md): How Origin Shield protects the origin with an allowlist of Azion's addresses and with HMAC signing.
- [Connector settings](/en/documentation/platform/connectors/settings.md#origin-shield): Every Origin Shield and HMAC field, its API name, its default, and its limits.
- [Set the Host header and path prefix for an origin](/en/documentation/guides/application-development/getting-started/set-the-host-header-and-path-prefix.md): Choose the Host header a connector sends and the path it prepends to every request.
- [Troubleshoot Connectors](/en/documentation/platform/connectors/troubleshooting.md): Fix a connector whose origin refuses requests or answers with an error.
