SignatureDoesNotMatch Error When Uploading Images to Shopify Staged Uploads API

Hi everyone,

I’m trying to upload images to Shopify using the stagedUploadsCreate mutation, but I keep running into a SignatureDoesNotMatch error when making the POST request to the staged upload URL.

I’ve followed the recommended process outlined in Shopify’s API docs, but the upload consistently fails. I’d appreciate any insights into what might be going wrong.

:hammer_and_wrench: Steps I’m Following:

  1. Request a staged upload URL using stagedUploadsCreate :white_check_mark: Success

  2. Upload the image to the staged URL using cURL :cross_mark: Fails with SignatureDoesNotMatch

  3. Register the file with Shopify using fileCreate (Not reached due to error in Step 2)

GraphQL Request for Staged Upload URL

mutation stagedUploadsCreate($input: [StagedUploadInput!]!) {
  stagedUploadsCreate(input: $input) {
    stagedTargets {
      url
      resourceUrl
      parameters {
        name
        value
      }
    }
    userErrors {
      field
      message
    }
  }
}

Variables:

{
  "input": [
    {
      "filename": "22208.png",
      "mimeType": "image/png",
      "resource": "IMAGE",
      "fileSize": "24567"
    }
  ]
}

Response from Shopify (stagedUploadsCreate)

{
  "data": {
    "stagedUploadsCreate": {
      "stagedTargets": [
        {
          "url": "https://shopify-staged-uploads.storage.googleapis.com/tmp/53086781626/files/5777d842-d5d2-4c25-9fd0-7f3eb6a8880f/22208.png?X-Goog-Algorithm=GOOG4-RSA-SHA256...",
          "resourceUrl": "https://shopify-staged-uploads.storage.googleapis.com/tmp/53086781626/files/5777d842-d5d2-4c25-9fd0-7f3eb6a8880f/22208.png",
          "parameters": [
            {
              "name": "content_type",
              "value": "image/png"
            },
            {
              "name": "acl",
              "value": "private"
            }
          ]
        }
      ],
      "userErrors": []
    }
  }
}

:desktop_computer: My PHP Upload Code

$stagedTarget = $stagedUploadResponse['body']['data']['stagedUploadsCreate']['stagedTargets'][0];

$uploadUrl = $stagedTarget['url'];
$uploadParams = [];

foreach ($stagedTarget['parameters'] as $param) {
    $uploadParams[$param['name']] = $param['value'];
}

// Attach file correctly
$uploadParams['file'] = new CURLFile(
    realpath($image_path),
    mime_content_type($image_path),
    basename($image_path)
);

echo "Uploading to: " . $uploadUrl . "\n";
print_r($uploadParams);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $uploadUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $uploadParams);
curl_setopt($ch, CURLOPT_HTTPHEADER, []); // Removed Content-Type to let cURL set it automatically

$response = curl_exec($ch);
$uploadError = curl_error($ch);
$http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo "HTTP Status: $http_status\n";
echo "Upload Response: " . json_encode($response, JSON_PRETTY_PRINT) . "\n";

if ($uploadError || $http_status !== 200) {
    echo "❌ Error uploading image: $uploadError\n";
    exit();
}

:warning: Error Response from Staged Upload

{
  "Code": "SignatureDoesNotMatch",
  "Message": "Access denied.",
  "Details": "The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.",
  "StringToSign": "GOOG4-RSA-SHA256\n20250313T184208Z\n20250313/auto/storage/goog4_request\n05487618751a65ebf3d17799588490225e8f83d7320f5cf64e39b7c1ff142580",
  "CanonicalRequest": "POST\n/tmp/53086781626/files/5777d842-d5d2-4c25-9fd0-7f3eb6a8880f/22208.png\nX-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=merchant-assets@shopify-tiers.iam.gserviceaccount.com%2F20250313%2Fauto%2Fstorage%2Fgoog4_request..."
}

:magnifying_glass_tilted_right: Things I’ve Tried

  1. Ensured all parameters from stagedUploadsCreate are passed as received :white_check_mark:

  2. Removed manual Content-Type header from cURL request :white_check_mark:

  3. Used absolute file paths in CURLFile :white_check_mark:

  4. Checked file permissions & existence :white_check_mark:

  5. Tried sleeping 5 seconds before making the upload request :white_check_mark:


:red_question_mark: Questions

• Am I missing any required parameters when sending the file to Shopify’s staged upload URL?

• Do I need to sign the request differently?

• Is there a specific cURL setting required to ensure the signature matches?

Any guidance would be greatly appreciated! Thanks in advance.

You’re missing parameters from the curl request including the field signature.

There’s a guide here which shows all the parameters Manage media for products

Hi Jordan, I’m including all the parameters in the curl request that I’ve received from Shopify. How do I include the field signature?

Hey @Ashley_Kennerley it should be returned by Shopify when you create the staged target. Please can you check what API version you are on?

I was using 2024-10 but I’ve just switched to 2025-01 and get the same response.

I don’t know PHP curl very well, are you doing a HTTP Put?
As that’s the default allowed http Method for image upload if you don’t specify it in the staged upload input.

Alternatively if you set httpMethod in your input to POST then that will allow you to use a post request which is supported for all media types

That’s great - I hadn’t included “httpMethod” => “POST”, in my staged upload variables. Thanks for your help!

1 Like