Many AWS services (like a managed Elasticsearch/OpenSearch cluster) don't accept
a simple API key — they require every HTTP request to be signed with SigV4
(Signature Version 4). The signer hashes the method, URL, headers, and body
together with a secret key and a timestamp, producing an Authorization header.
The server independently recomputes the same hash; if anything about the
request changes after signing (a header gets rewritten, the body gets
re-encoded), the hashes no longer match and the request is rejected with a
signature-mismatch error — even though nothing was "hacked," just mutated
in transit.
On servers running inside AWS (EC2/ECS/Lambda), the signer doesn't use a
long-lived access key. It uses STS (Security Token Service) to get
temporary credentials from the instance/task's IAM role: an access key, a
secret key, and a session token, all valid for a short window and
refreshed automatically. The session token has to ride along in an
X-Amz-Security-Token header — a correctly-signed request with a missing or
expired token still gets rejected.
A typical signing hook, wired into WordPress's outgoing HTTP filter so any request to the AWS host gets signed transparently:
$signer = new SignatureV4( 'es', $region );
$credentials = call_user_func( CredentialProvider::defaultProvider() )->wait();
$signed_request = $signer->signRequest( $request, $credentials );
// $signed_request now carries Authorization, X-Amz-Date,
// and (for temporary creds) X-Amz-Security-Token headers.
Because signing must happen on the exact bytes that get sent, sign the request last — right before transmission — not earlier in the request-building pipeline.
AWS_DEBUG=1 or an SDK's debug/logging option), and find the Authorization, X-Amz-Date, and (if using temporary creds) X-Amz-Security-Token headers in the outgoing request.
Done when: you can point to all three headers in the raw request log.