0002-00000040

更新时间:
复制 MD 格式

Problem description

OSS returns SignatureDoesNotMatch when the signature in your request does not match the signature OSS calculated on the server side. This is a signing issue, not a permissions issue — if your credentials lack the required permissions, OSS returns AccessDenied instead.

<?xml version="1.0" encoding="UTF-8"?>
<Error>
  <Code>SignatureDoesNotMatch</Code>
  <Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
  <RequestId>646DCB189AE2D1333018****</RequestId>
  <HostId>bucket.oss-cn-hangzhou.aliyuncs.com</HostId>
  <OSSAccessKeyId>LTAI******** </OSSAccessKeyId>
  <SignatureProvided>tPN3LTAI******** </SignatureProvided>
  <StringToSign>PUT\n\n\nTue, 23 May 2023 15:24:55 GMT\n/bucket/?acl</StringToSign>
  <StringToSignBytes>50 55 54 0A 0A 0A 54 75 65 2C 20 32 33 20 4D 61 79 20 32 30 32 33 20 31 35 3A 32 34 3A 35 35 20 47 4D 54 0A 2F 64 69 6E 61 72 79 2F 3F 61 63 6C </StringToSignBytes>
  <EC>0002-00000040</EC>
</Error>

Causes

When your client calls an OSS API operation, it must include a signature so OSS can verify the caller's identity. OSS returns SignatureDoesNotMatch when the StringToSign your client constructed differs from the one OSS computed from the incoming request.

Common causes include:

  • Invalid or mismatched AccessKey ID or AccessKey secret

  • Incorrect signature algorithm or missing fields in StringToSign

  • A client proxy or toolkit silently adding or modifying request headers after signing

  • Alibaba Cloud CDN converting a HEAD request to a GET request on CDN-accelerated domains

  • CDN injecting the x-oss-range-behavior: standard header into back-to-origin requests

  • CNAME enabled in DNS but not in the SDK client initialization

  • A plus sign (+) in the URL signature that was not URL-encoded

Examples

Example 1: CNAME disabled in SDK initialization

The following PHP code maps a custom domain to a bucket using CNAME but does not enable CNAME in the client constructor. The signing logic uses the wrong host, causing a signature mismatch.

<?php
if (is_file(__DIR__ . '/../autoload.php')) {
    require_once __DIR__ . '/../autoload.php';
}
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
    require_once __DIR__ . '/../vendor/autoload.php';
}

use OSS\OssClient;
use OSS\Core\OssException;

$accessKeyId     = getenv("OSS_ACCESS_KEY_ID");
$accessKeySecret = getenv("OSS_ACCESS_KEY_SECRET");
$endpoint        = "https://your.cname.com";

try {
    $isCNAME   = false;  // Bug: set to true to enable CNAME
    $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, $isCNAME);
} catch (OssException $e) {
    print $e->getMessage();
}

Example 2: Plus sign in URL signature not URL-encoded

The following signed URL contains a raw + in the signature. HTTP treats + as a space in query strings, so OSS receives a different signature value than the one the client calculated.

GET /oss.jpg?OSSAccessKeyId=nz2p**********&Expires=1141889120&Signature=ab+cd HTTP/1.1
Host: oss-example.oss-cn-hangzhou.aliyuncs.com
Date: Fri, 24 Feb 2012 06:38:30 GMT

Example 3: CDN converts HEAD to GET

When you use a CDN-accelerated domain to send a HEAD request, CDN automatically converts it to a GET request before forwarding to OSS. The HTTP verb mismatch causes SignatureDoesNotMatch.

HEAD /ObjectName?objectMeta HTTP/1.1
Host: your.cdn.com
Date: Fri, 24 Feb 2012 06:38:30 GMT
Authorization: OSS qn6q**************:77Dv****************

Solutions

Follow these steps to identify and fix the signature mismatch.

Step 1: Verify your AccessKey ID and AccessKey secret

Log in to ossbrowser with your AccessKey ID and AccessKey secret to confirm the credentials are valid. See Install and log on to ossbrowser.

Step 2: Verify the signature algorithm

OSS supports two signing methods. Use OSS SDKs to avoid calculating signatures manually — see Overview.

Method 1: Include the signature in the Authorization header

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

For details, see Include signatures in the Authorization header.

Method 2: Add the signature to a URL

StringToSign = VERB + "\n"
              + CONTENT-MD5 + "\n"
              + CONTENT-TYPE + "\n"
              + EXPIRES + "\n"
              + CanonicalizedOSSHeaders
              + CanonicalizedResource
Signature = urlencode(base64(hmac-sha1(AccessKeySecret, StringToSign)))

For details, see Add signatures to URLs.

Step 3: Compare the StringToSign values

The StringToSign field in the error response contains the exact string OSS signed on the server side. Compare it against the string your client constructed.

Example request:

PUT /bucket/abc?acl
Date: Wed, 24 May 2023 02:12:30 GMT
Authorization: OSS qn6q**************:77Dv****************
x-oss-abc: mymeta

Resulting StringToSign — annotated:

PUT\n                             /* HTTP verb */
\n                                /* Content-MD5 (empty) */
\n                                /* Content-Type (empty) */
Wed, 24 May 2023 02:12:30 GMT\n   /* Date */
x-oss-abc:mymeta\n                /* CanonicalizedOSSHeaders */
/bucket/abc?acl                   /* CanonicalizedResource */

Compact form (what the error response returns):

PUT\n\n\nWed, 24 May 2023 02:12:30 GMT\nx-oss-abc:mymeta\n/bucket/abc?acl
OSS includes all request headers with the x-oss- prefix in CanonicalizedOSSHeaders. Toolkits and proxies sometimes silently insert headers — such as Content-Type — without your knowledge. If you find an unexpected header in the server-side StringToSign, check whether your proxy or SDK is adding it.

Step 4: Apply the fix for your scenario

ScenarioFix
WeChat mini program returns a signature error, but the same request from a browser succeedsFollow step 3 to compare StringToSign. If the mini program request includes Content-Type, make sure Content-Type is included in signature calculation.
A client proxy modifies the signed request (for example, by adding a header)Follow step 3 to compare the StringToSign in the error response with what your client constructed. Toolkits and proxies sometimes silently insert headers such as Content-Type.
Alibaba Cloud CDN converts a HEAD request to a GET request when using a CDN-accelerated domainUse the default OSS domain to send the HEAD request directly, or configure a custom back-to-origin header Ali-Swift-Fwd-Head: on (see Configure custom request headers (old)).
Important

This configuration applies to all HEAD requests on the domain.

CDN adds the x-oss-range-behavior: standard header to back-to-origin requestsInclude x-oss-range-behavior: standard in the client-side request when calculating the signature.
A CNAME record maps a custom domain to a bucket, but CNAME is disabled in the SDK initializationEnable CNAME. For OSS SDK for Java, set setSupportCnam to true. For other SDKs, see Overview.
The signed URL contains a plus sign (+) in the signatureURL-encode the signature before sending the request. Replace + with %2B.

Example: URL-encoded signature (correct)

GET /oss.jpg?OSSAccessKeyId=nz2p**********&Expires=1141889120&Signature=ab%2Bcd HTTP/1.1
Host: oss-example.oss-cn-hangzhou.aliyuncs.com
Date: Fri, 24 Feb 2012 06:38:30 GMT

Example: CNAME initialization with CNAME enabled (PHP)

<?php
if (is_file(__DIR__ . '/../autoload.php')) {
    require_once __DIR__ . '/../autoload.php';
}
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
    require_once __DIR__ . '/../vendor/autoload.php';
}

use OSS\OssClient;
use OSS\Core\OssException;

$accessKeyId     = getenv("OSS_ACCESS_KEY_ID");
$accessKeySecret = getenv("OSS_ACCESS_KEY_SECRET");
$endpoint        = "https://your.cname.com";

try {
    $isCNAME   = true;  // Set to true to enable CNAME
    $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, $isCNAME);
} catch (OssException $e) {
    print $e->getMessage();
}