Every time you make a request to an AWS API, whether through the AWS SDK, the CLI, or a direct HTTP call, that request is signed with a process called Signature Version 4. SigV4 uses HMAC-SHA256 in a multi-step chain to produce a signature that proves the request came from someone with your AWS secret access key.
The process looks intimidating at first glance, but it is a series of straightforward HMAC computations. Each step serves a specific security purpose. Let's walk through it.
Why AWS Signs Every Request
You might wonder why AWS needs request signing when it already uses HTTPS. HTTPS encrypts the connection, so the request body and headers are protected from eavesdropping. But HTTPS does not authenticate the request to a specific AWS account.
AWS needs to know which account is making each request, for access control (does this account have permission to perform this action?), billing (which account should be charged?), and rate limiting (which account is hitting the rate limit?). It also needs to ensure the request was not modified in transit, including the headers that control which service and region receives the request.
HMAC signing solves all of this. The signature proves the requester has the secret key, covers the entire request (method, path, headers, body), and is scoped to a specific service, region, and date so it cannot be reused for a different purpose.
The SigV4 Signing Process
SigV4 produces a signature through five steps. Let's walk through each one.
### Step 1: Create the Canonical Request
The first step is to build a "canonical request," which is a standardized string representation of the HTTP request. It includes:
- The HTTP method (GET, POST, etc.) - The canonical URI (the path, normalized) - The canonical query string (sorted by key name) - The canonical headers (sorted by name, lowercased, trimmed) - The signed headers list (the names of the headers included in the signature) - The hash of the request body (SHA-256, hex-encoded)
The canonicalization is critical because the signature covers this exact string. If any byte differs between what the client signs and what AWS computes, the signature will not match. This is why the query string must be sorted, headers must be lowercased, and whitespace must be normalized.
The body is hashed rather than included directly because the body can be large. The hash is a fixed-size representation that fits in the canonical request.
### Step 2: Create the String to Sign
The second step creates a "string to sign," which is what the final HMAC will be computed over. It includes:
- The algorithm identifier ("AWS4-HMAC-SHA256") - The request timestamp (in ISO 8601 format) - The credential scope (date/region/service/aws4_request) - The hash of the canonical request (SHA-256, hex-encoded)
The credential scope is important. It binds the signature to a specific date, AWS region, and service. This means a signature for S3 in us-east-1 cannot be reused for EC2 in eu-west-1. The date scoping means a signature is only valid for a limited time (AWS recommends rejecting signatures more than 15 minutes old).
### Step 3: Calculate the Signing Key
This is where the HMAC chain happens. AWS does not use your secret access key directly as the HMAC key. Instead, it derives a signing key through a chain of HMAC computations:
kDate = HMAC("AWS4" + secretKey, date) kRegion = HMAC(kDate, region) kService = HMAC(kRegion, service) kSigning = HMAC(kService, "aws4_request")
Each step takes the output of the previous HMAC as the key for the next one. The final value, kSigning, is the key used to sign the request.
Why the chain? It scopes the key to a specific date, region, and service. Even if a signing key were somehow leaked, it would only be useful for one service in one region on one day. This is defense in depth: if one layer is compromised, the others still limit the damage.
### Step 4: Calculate the Signature
The signature is the HMAC-SHA256 of the string to sign using the signing key:
signature = HMAC-SHA256(kSigning, stringToSign)
This produces a 64-character hex string that is included in the Authorization header of the request.
### Step 5: Add the Authorization Header
The final step is to add the Authorization header to the request. It looks like:
Authorization: AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20260923/us-east-1/s3/aws4_request, SignedHeaders=host;range;x-amz-date, Signature=fe5f80f77d5fa3d8...
This header includes the access key ID, the credential scope (so AWS knows which key to use for verification), the list of signed headers (so AWS knows which headers to include in its canonical request), and the signature itself.
Why the HMAC Chain Matters
The multi-step HMAC key derivation is the most distinctive feature of SigV4. It would be simpler to just use the secret key directly as the HMAC key, like Stripe does with webhooks. The chain adds important security properties:
**Date scoping.** The first HMAC includes the date, so the signing key changes every day. This limits the usefulness of a leaked signing key to a single day.
**Region scoping.** The second HMAC includes the region, so a signing key for us-east-1 cannot be used for eu-west-1. This contains key compromise to a single region.
**Service scoping.** The third HMAC includes the service name, so a signing key for S3 cannot be used for EC2. This prevents cross-service attacks.
If an attacker somehow obtains a signing key, they can only sign requests for one service, in one region, on one day. This is much better than having the raw secret key, which could sign any request for any service in any region indefinitely.
Clock Skew and Replay Prevention
SigV4 includes a timestamp in the request (the X-Amz-Date header). AWS uses this to reject requests that are too old (typically more than 15 minutes) or too far in the future. This prevents replay attacks where an attacker captures a signed request and resends it later.
This means your server's clock must be reasonably accurate. If your clock is off by more than 15 minutes, AWS will reject your requests with a RequestTimeTooSkewed error. This is why time synchronization (NTP) is important for any server making AWS API calls.
The SDK Handles All of This
You rarely need to implement SigV4 manually. Every AWS SDK handles the entire signing process automatically. You provide your access key ID and secret access key (usually via environment variables, IAM roles, or credential files), and the SDK signs every request.
Understanding the process is valuable for debugging (when you get a SignatureDoesNotMatch error), for security awareness (understanding what your credentials can do), and for building custom integrations with AWS services that do not have SDK support.
For the fundamentals of HMAC that underlie SigV4, read our HMAC vs hash explained guide. And to compute HMACs yourself, try our HMAC generator.
The Bottom Line
AWS SigV4 is a well-designed signing process that uses HMAC-SHA256 in a chain to scope signatures to specific services, regions, and dates. Each step in the chain adds a layer of containment that limits the damage from key compromise. The process looks complex, but it is a series of simple HMAC computations, each building on the last.
You will almost always use the SDK to handle signing, but understanding what happens under the hood helps you debug issues, appreciate the security design, and make informed decisions about credential management.