How to sign an AWS API request with SigV4 using curl
You don't need an SDK to sign a request
Since version 7.75.0, curl has an --aws-sigv4 option that computes the AWS Signature Version 4 for you. It's the fastest way to check whether an IAM-protected endpoint works before you write any code.
Check your version first:
`bash
curl --version # needs 7.75.0 or later
`
The basic command
`bash
curl --aws-sigv4 "aws:amz:us-east-1:execute-api" \
--user "$AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY" \
https://abc123.execute-api.us-east-1.amazonaws.com/prod/orders
`
The argument has the form provider1:provider2:region:service:
aws:amzis the provider pair for AWS. It produces theAWS4-HMAC-SHA256algorithm andx-amz-*headers.us-east-1is the region the endpoint lives in.execute-apiis the signing service name, not the hostname. Common values ares3,execute-api(API Gateway),lambda,dynamodbandes(OpenSearch).
--user supplies the access key and secret key. curl uses them to sign the request. It doesn't send them as Basic auth.
Examples
Read an S3 object:
`bash
curl --aws-sigv4 "aws:amz:eu-west-1:s3" \
--user "$AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY" \
https://my-bucket.s3.eu-west-1.amazonaws.com/data.json
`
POST JSON to an IAM-authorized API Gateway route:
`bash
curl --aws-sigv4 "aws:amz:us-east-1:execute-api" \
--user "$AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY" \
-H "Content-Type: application/json" \
-d '{"sku":"A-100","qty":2}' \
https://abc123.execute-api.us-east-1.amazonaws.com/prod/orders
`
Use temporary credentials (SSO, an assumed role, or STS): add the session token as a header.
`bash
curl --aws-sigv4 "aws:amz:us-east-1:execute-api" \
--user "$AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY" \
-H "x-amz-security-token: $AWS_SESSION_TOKEN" \
https://abc123.execute-api.us-east-1.amazonaws.com/prod/orders
`
If you use AWS CLI profiles, you can export short-lived credentials into your shell first. The AWS CLI v2 command aws configure export-credentials --format env prints them as environment variables.
Read the response properly
Add -v to see what curl actually sent: the Authorization header, x-amz-date and the signed header list. AWS error bodies tell you what went wrong:
The request signature we calculated does not match the signature you provided: wrong secret key, wrong region or service name, or the request changed after signing.The security token included in the request is invalidor... expired: the session token is missing, mismatched or expired.Credential should be scoped to a valid region: the region in--aws-sigv4doesn't match the endpoint.User: arn:aws:... is not authorized to perform ...: the signature is fine. This is an IAM permissions issue, not a signing issue.
The last distinction saves a lot of time. A 403 whose message names your ARN means authentication worked.
Common pitfalls
1. Hostname used as the service name. The service is execute-api, not abc123.execute-api.
2. Region left at a default. The region must match the endpoint. Custom domains don't show it, so look it up.
3. --user mixed with other auth. --aws-sigv4 overrides the other auth types, so drop --basic, --digest or an Authorization header you added by hand.
4. Secrets in shell history. Use environment variables rather than pasting keys inline.
5. Clock skew. SigV4 includes a timestamp. If your machine's clock is badly off, valid credentials still get rejected.
6. Old curl. Some distributions or containers ship versions older than 7.75.0, and the option simply won't exist.
When curl isn't enough
I built Presign Pad for this: Sign an AWS request. See exactly why it fails. It runs in your browser and the free part works today.