Object Storage 403 Forbidden when using HEAD method
When I try to request an object using the HEAD
method I get a 403 error but when accessing the same signed URL using GET
it works.
I did a s3cmd info s3://streaming
and it comes back with
s3://streaming/ (bucket):
Location: default
Payer: BucketOwner
Expiration Rule: none
Policy: none
CORS: b'<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><CORSRule><AllowedMethod>GET</AllowedMethod><AllowedMethod>PUT</AllowedMethod><AllowedMethod>DELETE</AllowedMethod><AllowedMethod>HEAD</AllowedMethod><AllowedMethod>POST</AllowedMethod><AllowedOrigin>*</AllowedOrigin><AllowedHeader>*</AllowedHeader></CORSRule></CORSConfiguration>'
I can see HEAD
is allowed
We are moving from S3 and used our tool to verify that files are accessible using signed URLs, it worked on S3 but Linode seems to be blocking HEAD
Test URL that expires in 2022
https://us-east-1.linodeobjects.com/streaming/test.png?AWSAccessKeyId=U2EGDMGWC35UK51BACHC&Expires=1641101246&Signature=tCEp7f5nKJd4gzUZp20VmSO5VP0%3D
5 Replies
✓ Best Answer
Hi @RippleDon,
Looking at the AWS S3 documentation, the verb (HEAD/GET) is part of the signature, so this would required a different signature for a HEAD request to a GET.
I believe the GetPreSignedURL
method does have a way to pass the verb into the request - looking at this documentation.
In fact the default value for the Verb property is GET
- explaining why this works with a GET
request but not a HEAD
.
This Stack Overflow post suggests a similar workaround you have come up with of using the Range
header with a GET
request if you cannot generate different signatures for the HEAD
and GET
requests.
After some searching I found this related Community Questions Site post – 'Access-Control-Allow-Origin' doesn't appear in the object storage! – which mentions that, “the Access-Control-Allow-Origin header will only appear if your request's Origin domain also matches the domain listed in your Access-Control-Allow-Origin.” Is there a chance that the header in the request was set to a different domain? There are also a couple of other useful tests in the post that may be helpful in your case as well. Feel free to post your tests and any results which you feel are relevant. Providing the header request originally run might also help the community to reach a solution with more accuracy.
I'm actually sending the request from a C# desktop app so I was not setting an Origin
header, I tested by adding in one that was set to our main sites domain as well as tried localhost, I also tried in Postman, all scenarios gave the same 403 Forbidden
response code
Below is the code I'm using to check, the commented out Range
header is a workaround I found that works with using it with a GET
request. But the code should technically not matter as even a simple HEAD
request in postman results in a 403, The code and method works on the same objects that are still in our S3 bucket
string statusCode;
//Generate signed URL
var signedLink = s3Client.GetPreSignedURL(new GetPreSignedUrlRequest
{
BucketName = bucketName,
Key = lesson["url"].ToString(),
Expires = DateTime.Now.AddHours(1)
});
var webRequest = WebRequest.Create(signedLink);
webRequest.Method = "HEAD";
webRequest.Headers.Add("Origin", "https://www.########.com");
//webRequest.Headers.Add("Range", "bytes=0-1"); //Works when using GET
try
{
using HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
statusCode = webResponse.StatusCode.ToString();
}
catch (WebException we)
{
statusCode = ((HttpWebResponse)we.Response).StatusCode.ToString();
}
lesson["result"] = statusCode;
Did another test with a public file and HEAD
works fine on it yet when I try to access it with a signed URL I get the dreaded 403 Forbidden
error. This really feels like an issue with Object Storage and its blocking HEAD
requests when accessed with a signed URL.
That was it! I guess CloudFront does not require that you set the verb
in the String to Sign
, so when I converted the code from a CloudFront signed URL to an S3 Signed URL, I figured it would work the same.
And for anyone looking for C# signed URL code here you go
//Generate signed URL
var signedLink = s3Client.GetPreSignedURL(new GetPreSignedUrlRequest
{
BucketName = bucketName,
Key = lesson["url"].ToString(),
Expires = DateTime.Now.AddHours(1),
Verb = HttpVerb.HEAD
});