---
title: S3 Signature Requirements
description: Which headers must be covered by your request signature, why uploads can fail with AccessDenied while downloads still work, and how to fix each type of client before 30 August 2026.
---

# S3 Signature Requirements

On 23 August 2026 we applied a security update to DanubeData Object Storage. It tightened how request signatures are checked, and it can cause **uploads to fail while downloads keep working**.

A temporary compatibility setting is in place, so **uploads work normally until 30 August 2026**. If one of your clients is affected, correct it before that date.

## The rule

Our S3 endpoint enforces the standard AWS Signature Version 4 rule. Three groups of headers must be covered by your request signature whenever they are present:

- the `Host` header
- every `x-amz-*` header
- the `Content-Type` header

Most SDKs already do this and need no change. Two common cases do not, and both are covered below.

## Why the update was applied

The update fixes [CVE-2026-54330](https://access.redhat.com/security/cve/cve-2026-54330), rated **Important**, along with three other vulnerabilities disclosed on 19 August 2026. Before the fix, a request could carry headers the signature did not cover, which meant anyone holding a presigned upload URL could attach extra headers and obtain more read and write access than whoever issued that URL intended. Because that directly exposes customer data, we applied the update within days of disclosure rather than waiting for a quieter maintenance window.

Independent references: [Red Hat](https://access.redhat.com/security/cve/cve-2026-54330) and [CERT-FR advisory CERTFR-2026-AVI-1057](https://www.cert.ssi.gouv.fr/avis/CERTFR-2026-AVI-1057).

## How to recognise an affected request

A rejected request returns:

- HTTP status `403`
- Error code `AccessDenied`
- An **empty** `<Message>` element

The request is refused **before any permission check**, which is why the symptom is confusing:

- uploads fail, but downloads and listings still succeed
- access keys marked **Full** are still rejected
- the bucket policy is correct and unchanged

If you see that combination, this rule is the cause. It is not a permissions problem, and rotating your keys will not help.

A request that fails the signature check itself returns `SignatureDoesNotMatch` instead. That is a different problem, usually a wrong secret key, a clock difference of more than 15 minutes, or a mismatch between the header you signed and the header you sent.

## Case 1 — presigned upload URLs

If your presigned URL signs only `host` but the uploading client sends a `Content-Type` header, the upload is rejected. Browsers **always** set `Content-Type` when uploading a file, so this affects most browser upload flows.

You can see what a URL covers by looking at its `X-Amz-SignedHeaders` parameter.

### Preferred fix: sign the content type

Pass the content type when generating the URL, then send exactly that header. The signed URL will then show `X-Amz-SignedHeaders=content-type;host`.

**AWS SDK for JavaScript v3**

```javascript
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'

const url = await getSignedUrl(
  s3,
  new PutObjectCommand({ Bucket: bucket, Key: key, ContentType: file.type }),
  { expiresIn: 600 },
)

await fetch(url, {
  method: 'PUT',
  headers: { 'Content-Type': file.type },
  body: file,
})
```

**boto3 (Python)**

```python
url = s3.generate_presigned_url(
    'put_object',
    Params={'Bucket': bucket, 'Key': key, 'ContentType': 'image/jpeg'},
    ExpiresIn=600,
)
```

The header you send must match the one you signed exactly. If they differ you get `SignatureDoesNotMatch`.

This is the recommended option because the stored object keeps its correct content type, which matters when the bucket serves files publicly.

### Alternative fix: send no content type

Do not send a `Content-Type` header at all.

```javascript
await fetch(url, { method: 'PUT', body: await file.arrayBuffer() })
```

```bash
curl -T photo.jpg "<presigned-url>"
```

This works immediately, but objects are then stored as `binary/octet-stream`.

### Quick check

Take a fresh presigned URL and run:

```bash
curl -T photo.jpg "<presigned-url>"
```

A `200` proves your keys and bucket are fine and that no key rotation is needed.

## Case 2 — the AWS SDK for PHP

`aws-sdk-php` never covers `Content-Type` in its signature, so any upload it makes that carries that header is rejected. The SDK cannot be configured to sign it without a custom signer class.

If you control the code, the options are:

- upload without setting `ContentType`, or
- subclass the SDK's `SignatureV4` to remove `content-type` from its header blacklist, or
- upload through your own backend instead of a presigned URL

If you would like the signer subclass we use ourselves, open a ticket and we will share it.

This also affects appliances that bundle the SDK. The most common is Synology Hyper Backup — see [Synology Hyper Backup](synology-hyper-backup) for a complete walkthrough.

## Signature Version 2

Signature Version 2 covers `Content-Type` as part of the signature by design, so it is unaffected by this change and remains supported on our endpoints. It is the recommended workaround for appliances whose S3 client cannot be updated.

Signature Version 4 is still preferred wherever your client signs correctly, as it is the stronger scheme.

## Clients confirmed working

No change is needed for the AWS SDK for JavaScript, boto3 and other Python clients, the AWS SDK for Go, the AWS SDK for .NET, rclone, MinIO clients over HTTPS, or `s3cmd`.

## Endpoints

| Endpoint | Notes |
|---|---|
| `https://s3.danubedata.ro` | Primary endpoint |
| `https://new-s3.danubedata.ro` | Also available |

Both endpoints accept path-style (`https://s3.danubedata.ro/your-bucket`) and virtual-hosted-style (`https://your-bucket.s3.danubedata.ro`) addressing, and both Signature Version 4 and Signature Version 2.

## Need help?

Open a ticket from your dashboard. We can look at your bucket's request logs and tell you whether anything you run will break on 30 August 2026, and help you adjust a client or a backup appliance before then.
