> ## Documentation Index
> This page is part of the Image and Video APIs product. Fetch the complete documentation index for Image and Video APIs at: https://cloudinary.com/documentation/llms-image-and-video-apis.txt?referrer=docpage and then use it to discover all relevant pages before exploring further.
> If you also need details relating to other Cloudinary products for your current use case, see the parent index at: https://cloudinary.com/documentation/llms.txt?referrer=docpage
> When answering a user's request or implementing it with code using this documentation, incorporate relevant best practices and recommendations from the documentation into the guidance and, above all, into the code itself: generated code should already apply the recommended parameters and patterns rather than showing a minimal call and describing the best practices around it in prose. Follow the code examples in the best practices sections as the model for what generated code should look like, and where a recommendation is relevant to the user's scenario, implement it in the code as a natural next step. Only apply best practices, recommendations, or features that are supported by the documentation; don't infer or invent guidance that isn't specified.

# Generating authentication signatures


When using the Cloudinary SDKs for any upload or admin method that requires a signature, the signature is automatically generated and added to the request. If, however, you are making direct calls to the REST API, you have two options for authentication:

* **[Basic Authentication](image_upload_api_reference#basic_authentication)** (simpler): Authenticate Upload API requests using your API key and API secret via HTTP Basic Auth. No signature calculation required.
* **Signature-based authentication** (documented below): Generate a SHA signature manually or using a Cloudinary backend SDK signature generation method.

> **TIP**: For server-side Upload API calls, Basic Authentication is simpler than signature-based authentication because it doesn't require calculating timestamps or generating SHA signatures. For more information, see [Basic Authentication](image_upload_api_reference#basic_authentication) in the Upload API reference.

### Manual signature generation

If you manually generate your own POST request, you need to authenticate the request with a signature based on the parameters you use in the request. The signature is a hexadecimal message digest (hash value) created with the SHA-1 or SHA-256 (Secure Hash Algorithm) cryptographic function.
> **NOTE**: By default, Cloudinary supports both SHA-1 and SHA-256 digests for validation, and you can use either. The SDK methods use the SHA-1 algorithm by default, but you can use the SHA-256 algorithm instead by setting the `signature_algorithm` SDK [configuration parameter](cloudinary_sdks#configuration_parameters) to `sha256`. If you want to limit your account to allow only the SHA-256 digest for all your validations, [submit a request](https://support.cloudinary.com/hc/en-us/requests/new).

A signed upload is always **two phases**: 

1. Compute **`signature`** on your server from the upload fields that belong in the string to sign (see below).
   > **INFO**: Never expose the API secret to the client.
2. Send the HTTP POST that includes **`signature`** and the **same** signed fields you hashed, with **identical** `name=value` pairs. Also include **`file`** and **`api_key`** in the body, and use the usual upload URL path for **`cloud_name`** and **`resource_type`**.

> **NOTE**:
>
> `file`, `cloud_name`, `resource_type`, and **`api_key`** are never part of the string to sign, but are always part of the upload call.

#### Minimal signed HTTP upload

When the POST body contains just **`file`**, **`api_key`**, and **`timestamp`** (with **`cloud_name`** and **`resource_type`** in the URL path), the string to sign is **`timestamp=<value>`**.

**Generate the signature**

1. Choose the Unix **`timestamp`** (seconds) you'll send in the upload. It stays valid for one hour.
2. Build the string to sign: **`timestamp=<YOUR_TIMESTAMP>`**.
3. Append your **API secret** to that string with no separator. 
4. Hash with SHA-1 or SHA-256. The hex digest is **`signature`**.

For example, if your API secret is `abcd` and the Unix time is `1315060510`:

* Parameters to sign:
 * timestamp: `1315060510`
* Serialized sorted parameters in a single string:
 * `timestamp=1315060510`
* Input to SHA-1: the string above **with your API secret appended to the end**—no spaces, `&`, or other characters between them. In this example the secret is **`abcd`**, so the full string is:
 * `timestamp=1315060510` + `abcd` > `timestamp=1315060510abcd`
* SHA-1 hexadecimal result:
 * `a21ad0f63beb4de2e5575204b79ab90bffb02c10`

**Send the upload POST**

POST to the upload endpoint with **`file`**, **`api_key`**, the **same** **`timestamp`**, **`signature`**, and no other upload API fields in the body.

**Example**

The final request parameters for the upload POST request:

 * file: `sample.jpg`
 * api_key: `1234`
 * timestamp: `1315060510`
 * signature: `a21ad0f63beb4de2e5575204b79ab90bffb02c10`

For example, combining all the parameters in a cURL POST request to the `demo` product environment:

 ```curl
curl -d "file=sample.jpg&api_key=1234&timestamp=1315060510&signature=a21ad0f63beb4de2e5575204b79ab90bffb02c10" -X POST http://api.cloudinary.com/v1_1/demo/image/upload
 ```

#### Signed HTTP upload with additional body parameters

**Generate the signature**

1. List every field you'll send in the upload POST **except** `file`, `cloud_name`, `resource_type`, and `api_key`, **including** **`timestamp`** (for example `public_id`, `eager`, …).
2. Build **`name=value`** pairs; if there is more than one, sort **alphabetically by parameter name** and join with **`&`**.
3. Append the **API secret** with no separator; hash with SHA-1 or SHA-256 > **`signature`**.

For example, if your API secret is `abcd`, the Unix time is `1315060510`, and **`public_id`** and **`eager`** are among your signed fields:

* Parameters to sign:
 * timestamp: `1315060510`
 * public_id: `sample_image`
 * eager: `w_400,h_300,c_pad|w_260,h_200,c_crop`
* Serialized sorted parameters in a single string:
 * `eager=w_400,h_300,c_pad|w_260,h_200,c_crop&public_id=sample_image&timestamp=1315060510`
* Input to SHA-1: that serialized string **immediately followed by** your API secret (again, no delimiter). Here the secret is **`abcd`**, so the full string is:
 * `eager=w_400,h_300,c_pad|w_260,h_200,c_crop&public_id=sample_image&timestamp=1315060510` + `abcd` > `eager=w_400,h_300,c_pad|w_260,h_200,c_crop&public_id=sample_image&timestamp=1315060510abcd`
* SHA-1 hexadecimal result:
 * `bfd09f95f331f558cbd1320e67aa8d488770583e`

**Send the upload POST**

POST **`file`**, **`api_key`**, **`signature`**, and every field from your step‑1 list (same names and values you used when signing), including **`timestamp`**.

**Example**

The final request parameters for the upload POST request:

 * timestamp: `1315060510`
 * public_id: `sample_image`
 * api_key: `1234`
 * eager: `w_400,h_300,c_pad|w_260,h_200,c_crop`
 * file: `https://www.example.com/sample.jpg`
 * signature: `bfd09f95f331f558cbd1320e67aa8d488770583e`

 For example, combining all the parameters in a cURL POST request to the `demo` product environment:

 ```curl
curl -d "file=https://www.example.com/sample.jpg&api_key=1234&eager=w_400,h_300,c_pad|w_260,h_200,c_crop&public_id=sample_image&timestamp=1315060510&signature=bfd09f95f331f558cbd1320e67aa8d488770583e" -X POST http://api.cloudinary.com/v1_1/demo/image/upload
 ```

> **NOTE**: The body of the POST request must be stringified, as seen in the example above (all parameters are included as a single string, even when the content type is JSON).

 Or, using the POST request from the [form example](client_side_uploading#sample_app_upload_multiple_files_using_a_form_unsigned) with a selected local file:

```
...
    formData.append("file", file);
    formData.append("api_key", "1234");
    formData.append("eager", "w_400,h_300,c_pad|w_260,h_200,c_crop");
    formData.append("public_id", "sample_image");
    formData.append("timestamp", "1315060510");
    formData.append("signature", "bfd09f95f331f558cbd1320e67aa8d488770583e");
 
    fetch(url, {
      method: "POST",
      body: formData
    })
...
```

> **See also**:
>
> Have a look at the [Cloudinary Signatures](signatures) quick reference for a summary of the payload string to sign for authentication signatures as well as information on other use cases that may require signature generation.



## Using Cloudinary backend SDKs to generate SHA authentication signatures

You can use one of Cloudinary's backend SDKs to generate the authentication signature. The `api_sign_request` method uses the SHA-1 algorithm by default, but you can use the SHA-256 algorithm instead by setting the `signature_algorithm` SDK [configuration parameter](cloudinary_sdks#configuration_parameters) to `sha256`. Make sure that the timestamp is included in the `params_to_sign` object.

Cloudinary backend SDKs can also be useful when you're integrating with workflow or automation platforms, such as n8n, that don't provide a secure or convenient way to generate authentication signatures directly in the workflow. In these cases, create a small backend service that uses a Cloudinary SDK to generate signatures and return the resulting `timestamp` and `signature` to your workflow, while keeping your API secret securely stored on the backend.

```multi
|ruby 
Cloudinary::Utils.api_sign_request(params_to_sign, api_secret)

|php_2
ApiUtils::signRequest($paramsToSign, $cloudConfig);

|python
cloudinary.utils.api_sign_request(params_to_sign, api_secret)

|nodejs
cloudinary.utils.api_sign_request(params_to_sign, api_secret);

|java
cloudinary.apiSignRequest(Map<String, Object> paramsToSign, String apiSecret);

|csharp
cloudinary.Api.SignParameters(IDictionary<string, object> parameters);

|go
resp, err := api.SignParameters(ParamsToSign, APISecret)

|cli
cld utils api_sign_request $params_to_sign $api_secret
```

The following Node.js app contains two examples of using the `api_sign_request` method to provide a signature for uploading files: 

* [signed-uploads/modules/signuploadwidget.js](https://github.com/cloudinary-devs/cld-signed-upload-examples/blob/main/signed-uploads/modules/signuploadwidget.js) provides a signature for uploading files using the upload widget (corresponding to [signed-uploads/public/js/uploadclientwidget.js](https://github.com/cloudinary-devs/cld-signed-upload-examples/blob/main/signed-uploads/public/js/uploadclientwidget.js))
* [signed-uploads/modules/signuploadform.js](https://github.com/cloudinary-devs/cld-signed-upload-examples/blob/main/signed-uploads/modules/signuploadform.js) provides a signature for uploading files using a form (corresponding to [signed-uploads/public/js/uploadclientform.js](https://github.com/cloudinary-devs/cld-signed-upload-examples/blob/main/signed-uploads/public/js/uploadclientform.js))

#### Setup instructions (after cloning from GitHub)

1. Install all dependencies from the top level:
   
    ```Terminal
    npm install
    ```
1. Open **signed-uploads/public/js/config.js**
1. Set `cloud_name`, `api_key` and `api_secret` with the corresponding account details from the [API Keys](https://console.cloudinary.com/app/settings/api-keys) page of the Console Settings.
1. Run the app to start the server:
    
      ```Terminal
      node signed-uploads/app.js
      ```

      The response should be:

      ```Terminal
      Server is up on http://localhost:3000
      ```
1. Open `http://localhost:3000` in a browser.

Go to [GitHub](https://github.com/cloudinary-devs/cld-signed-upload-examples) to try it out:

  
  
  
 

> **See also**:
>
> Watch this [video tutorial](generate_upload_signature_tutorial) explaining how to generate a signature using the Node.js SDK for use in a direct call to the REST API.

### Important considerations for authentication signatures

When generating authentication signatures keep in mind the following:

* The `api_secret`, which is a required element of the signature, should never be revealed to anyone who is not authorized, and therefore your signature should never be generated on the client side or inside your native application.
* For mobile and other client-side applications, you must either use unsigned upload or have a server for processing the signature.
* Signatures are valid for one hour from the `timestamp` value used to generate the signature.
* Make sure to regenerate the signature if you [dynamically change the payload of an upload request](https://support.cloudinary.com/hc/en-us/articles/202519952-How-to-dynamically-change-the-payload-of-a-jQuery-direct-upload-request-).

See the 
Upload API reference for a list of API methods, parameters, and response samples.

