All Products
Search
Document Center

OpenSearch:Signature method of OpenSearch API V3

Last Updated:Apr 01, 2026

OpenSearch authenticates every API request using HMAC-SHA1 signatures. Each request must include an Authorization header containing your AccessKey ID and a computed signature derived from your AccessKey secret.

The OpenSearch SDKs for PHP, Python, and C# handle signature calculation automatically. The Java SDK is also available but does not implement signature calculation. If you use a PHP, Python, or C# SDK, skip this topic. Read on only if you are implementing a custom HTTP client or working in an unsupported language.

How it works

Each signed request goes through four construction stages before it reaches the OpenSearch server:

AccessKeySecret
      │
      ├─► Build CanonicalizedOpenSearchHeaders  ─┐
      │                                           │
      ├─► Build CanonicalizedResource            ─┤
      │                                           │
      ▼                                           ▼
   string-to-sign  ──► HMAC-SHA1 + Base64 ──► Signature
                                                  │
                                                  ▼
                                        Authorization header

The Authorization header has this format:

Authorization: OPENSEARCH <AccessKeyId>:<Signature>

A space is required after OPENSEARCH.

The signature is computed as:

Signature = base64(hmac-sha1(AccessKeySecret,
            VERB + "\n"
            + Content-MD5 + "\n"
            + Content-Type + "\n"
            + Date + "\n"
            + CanonicalizedOpenSearchHeaders
            + CanonicalizedResource))

Supported applications and protocols

  • Application types: Advanced applications, Standard applications

  • Protocol: HTTP only

  • Request methods: GET for search requests, POST for push-data requests

Prerequisites

Before you begin, make sure you have:

  • An AccessKey pair (AccessKey ID and AccessKey secret) issued through alibabacloud.com

  • An OpenSearch application (Advanced or Standard edition)

Keep your AccessKey secret strictly confidential. It is used to sign every request.

Step 1: Assemble the string-to-sign

The string-to-sign concatenates the following components in order:

ComponentRequiredNotes
VERBYesHTTP method: GET, POST, PUT, HEAD, or DELETE
Content-MD5ConditionalMD5 hash of the request body (e.g., 4991ef0788236a8f280fed0db928e74e). Leave blank for requests without a body, such as search requests. See RFC 2616.
Content-TypeNoExample: application/json
DateYesUTC timestamp in ISO 8601 format: YYYY-MM-DDThh:mm:ssZ. Example: 2019-02-25T10:09:57Z. Requests are rejected with HTTP 403 if the timestamp differs from the server time by more than 15 minutes.
CanonicalizedOpenSearchHeadersConditionalRequired when the request includes X-Opensearch-* headers. See Step 2.
CanonicalizedResourceYesThe request path (plus query string for search requests). See Step 3.

Each component is separated by a newline character (\n). Components with no value (such as Content-MD5 in a search request) contribute an empty string — the \n separator is still required.

Step 2: Build CanonicalizedOpenSearchHeaders

CanonicalizedOpenSearchHeaders covers all request headers whose names start with X-Opensearch-. The most common one is X-Opensearch-Nonce.

`X-Opensearch-Nonce` format: a 10-digit Unix timestamp followed by a 6-digit random number in the range 100000–999999. Example: 1551089397451704.

To build the string:

  1. List all X-Opensearch-* headers that have non-empty values.

  2. Sort them alphabetically by header name.

  3. Convert each header name to lowercase: X-Opensearch-Noncex-opensearch-nonce.

  4. Remove all spaces around the colon separator: x-opensearch-nonce : valuex-opensearch-nonce:value.

  5. Join all header-value pairs with \n, and append \n after the last pair.

Before/after example:

Before: X-Opensearch-Nonce : 1551089397451704
After:  x-opensearch-nonce:1551089397451704\n
If the request has no X-Opensearch-* headers, omit CanonicalizedOpenSearchHeaders from the string-to-sign entirely — do not include an empty line or a trailing \n in its place.

When adding these headers to the actual HTTP request, use the original mixed-case names (X-Opensearch-Nonce), not the lowercase form used in signature calculation.

Step 3: Build CanonicalizedResource

  • Search requests: CanonicalizedResource = path + ? + query string

  • Push-data requests: CanonicalizedResource = path only

Build the path

Encode the raw path string, then replace any %2F back to /. Replace app_schema_demo with your application name and tab with your table name.

Request typePath
Search/v3/openapi/apps/<app-name>/search
Suggestion search/v3/openapi/suggestions/<suggestion-name>/actions/search
Search by application ID/v3/openapi/apps/<app-id>
Push data/v3/openapi/apps/<app-name>/<table-name>/actions/bulk

Build the query string (search requests only)

  1. Drop parameters whose values are empty.

  2. Sort the remaining parameters alphabetically — first by key, then by value.

  3. Encode each key and value per RFC 3986, then join them with =.

  4. Join all encoded key-value pairs with &.

  5. Concatenate clauses within the query parameter using && before URL-encoding.

Example: The following query string:

fetch_fields=name&query=query%3Dname%3A%27%E6%96%87%E6%A1%A3%27%26%26sort%3Did%26%26config%3Dformat%3Afulljson

decodes to:

fetch_fields=name
query=query=name:'document'&&sort=id&&config=format:fulljson

The full CanonicalizedResource for this example:

/v3/openapi/apps/app_schema_demo/search?fetch_fields=name&query=query%3Dname%3A%27%E6%96%87%E6%A1%A3%27%26%26sort%3Did%26%26config%3Dformat%3Afulljson

Step 4: Compute the signature

Use HMAC-SHA1 as defined in RFC 2104. Encode all strings in UTF-8. If the string-to-sign contains non-ASCII characters, encode them to UTF-8 bytes before computing the HMAC.

Pseudocode:

string_to_sign = VERB + "\n"
               + Content-MD5 + "\n"
               + Content-Type + "\n"
               + Date + "\n"
               + CanonicalizedOpenSearchHeaders   # omit if no X-Opensearch-* headers
               + CanonicalizedResource

hmac_bytes = HMAC-SHA1(key=AccessKeySecret.encode("utf-8"),
                       msg=string_to_sign.encode("utf-8"))
Signature  = base64_encode(hmac_bytes)

Python 3 example:

import hmac
import base64

string_to_sign = '\n'.join([
    'GET',
    '',                          # Content-MD5 is blank for search requests
    'application/json',
    '2019-02-25T10:09:57Z',
    'x-opensearch-nonce:1551089397451704',
    '/v3/openapi/apps/app_schema_demo/search'
    '?fetch_fields=name'
    '&query=query%3Dname%3A%27%E6%96%87%E6%A1%A3%27%26%26sort%3Did%26%26config%3Dformat%3Afulljson'
])

signature_hmac = hmac.new(
    'yourAccessKeySecret'.encode('utf-8'),
    string_to_sign.encode('utf-8'),
    'sha1'
)
signature = base64.b64encode(signature_hmac.digest())

Step 5: Add the Authorization header

Combine your AccessKey ID and the computed signature:

headers['Authorization'] = 'OPENSEARCH ' + '<your-access-key-id>' + ':' + signature

Include all required headers in the HTTP request. The header values must exactly match the values used during signature calculation.

Required headers for search requests

Signature parameterRequiredRequest headerRequired
AccessKeySecretYesDateYes
VERBYesX-Opensearch-NonceYes
DateYesAuthorizationYes
x-opensearch-nonceYes
canonicalized_resourceYes

Required headers for push-data requests

Signature parameterRequiredRequest headerRequired
AccessKeySecretYesContent-MD5Yes
VERBYesDateYes
Content-MD5YesAuthorizationYes
DateYes
canonicalized_resourceYes

Including Content-MD5, Content-Type, Date, CanonicalizedOpenSearchHeaders, and Authorization in every request reduces the chance of errors caused by missing optional headers.

End-to-end example

This example walks through a signed GET search request step by step so you can verify each intermediate value.

Input values

FieldValue
AccessKey IDLTAI****************
AccessKey secretyourAccessKeySecret
Request methodGET
Content-MD5*(blank — no request body)*
Content-Typeapplication/json
Date2019-02-25T10:09:57Z
X-Opensearch-Nonce1551089397451704
Application nameapp_schema_demo

Step 2 result: CanonicalizedOpenSearchHeaders

x-opensearch-nonce:1551089397451704\n

Step 3 result: CanonicalizedResource

/v3/openapi/apps/app_schema_demo/search?fetch_fields=name&query=query%3Dname%3A%27%E6%96%87%E6%A1%A3%27%26%26sort%3Did%26%26config%3Dformat%3Afulljson

Step 1 result: string-to-sign

GET\n
\n
application/json\n
2019-02-25T10:09:57Z\n
x-opensearch-nonce:1551089397451704\n
/v3/openapi/apps/app_schema_demo/search?fetch_fields=name&query=query%3Dname%3A%27%E6%96%87%E6%A1%A3%27%26%26sort%3Did%26%26config%3Dformat%3Afulljson

Step 4 result: signature

1P7tfE****

Step 5 result: Authorization header

Authorization: OPENSEARCH LTAI****************:1P7tfE****

Full request headers

Content-MD5:
Content-Type: application/json
Date: 2019-02-25T10:09:57Z
X-Opensearch-Nonce: 1551089397451704
Authorization: OPENSEARCH LTAI****************:1P7tfE****

Full request URL

http://<host>/v3/openapi/apps/app_schema_demo/search?fetch_fields=name&query=query%3Dname%3A%27%E6%96%87%E6%A1%A3%27%26%26sort%3Did%26%26config%3Dformat%3Afulljson

Request URL format

The full request URL is host + CanonicalizedResource. Replace <host> with the OpenSearch API endpoint for your region.

Request typeURL
Searchhttp://<host>/v3/openapi/apps/<app-name>/search?<query-string>
Suggestion searchhttp://<host>/v3/openapi/apps/<app-name>/suggest/<suggestion-name>/search?hits=10&query=<encoded-query>
Search by application IDhttp://<host>/v3/openapi/apps/<app-id>
Push datahttp://<host>/v3/openapi/apps/<app-name>/<table-name>/actions/bulk

For push-data requests, include the data payload in the request body.

Application schema template

The following schema defines a Standard application named app_schema_demo. Use it as a starting point for your own application.

{
  "name": "app_schema_demo",
  "type": "standard",
  "schema": {
    "indexes": {
      "search_fields": {
        "id": { "fields": ["id"] },
        "name": { "fields": ["name"], "analyzer": "chn_standard" },
        "phone": { "fields": ["phone"], "analyzer": "fuzzy" },
        "int_arr": { "fields": ["int_arr"] },
        "literal_arr": { "fields": ["literal_arr"] },
        "cate_id": { "fields": ["cate_id"] }
      },
      "filter_fields": ["id", "int_arr", "literal_arr", "float_arr", "cate_id"]
    },
    "tables": {
      "tab": {
        "name": "tab",
        "fields": {
          "id":          { "name": "id",          "type": "INT",           "primary_key": true  },
          "name":        { "name": "name",        "type": "TEXT",          "primary_key": false },
          "phone":       { "name": "phone",       "type": "SHORT_TEXT",    "primary_key": false },
          "int_arr":     { "name": "int_arr",     "type": "INT_ARRAY",     "primary_key": false },
          "literal_arr": { "name": "literal_arr", "type": "LITERAL_ARRAY", "primary_key": false },
          "float_arr":   { "name": "float_arr",   "type": "FLOAT_ARRAY",   "primary_key": false },
          "cate_id":     { "name": "cate_id",     "type": "INT",           "primary_key": false }
        },
        "primary_table": true
      }
    },
    "route_field": null
  },
  "data_sources": [],
  "first_ranks": {},
  "second_ranks": {},
  "summary": [],
  "fetch_fields": ["id", "name", "phone", "int_arr", "literal_arr", "float_arr", "cate_id"],
  "quota": { "qps": 6, "doc_size": 0.3 }
}

SDK alternatives

The OpenSearch SDKs for Java, PHP, Python, and C# implement this signature method. The PHP, Python, and C# SDKs include working implementations you can reference when implementing signing in other languages.

OpenSearch does not maintain SDKs that users build from the official source code. Users are responsible for maintaining such implementations.