✓ Solved

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

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

    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


Please enter an answer

You can mention users to notify them: @username

You can use Markdown to format your question. For more examples see the Markdown Cheatsheet.

> I’m a blockquote.

I’m a blockquote.

[I'm a link] (https://www.google.com)

I'm a link

**I am bold** I am bold

*I am italicized* I am italicized

Community Code of Conduct