OVHcloud Public Cloud Status

Current status
Legend
  • Operational
  • Degraded performance
  • Partial Outage
  • Major Outage
  • Under maintenance

[GLOBAL][Storage] - Object Storage S3 maintenance notification

Scheduled Maintenance Report for Public Cloud

Completed

The scheduled maintenance has been completed.
Posted Jul 28, 2026 - 16:56 UTC

In progress

Scheduled maintenance is currently in progress. We will provide updates as necessary.
Posted Jul 28, 2026 - 16:40 UTC

Scheduled

As part of our continuous improvement and security enhancement efforts, we are rolling out a security fix for our Object Storage S3 offering.

This update addresses an authorization validation issue related to AWS Signature Version 4 (SigV4).

Previously, requests could include additional x-amz-* headers that were not included in the signature validation process.
In some cases, this could allow a presigned URL request to be modified by adding unsigned, meaningful headers (such as x-amz-copy-source), potentially altering the intended operation.

Following this update, our gateway will enforce the same behavior as AWS S3: all x-amz-* headers included in a request must be part of the SignedHeaders list.

Requests containing unsigned x-amz-* headers will now be rejected.

Start time : 27/07/2026 08:00 UTC
End time : 28/07/2026 10:00 UTC
Service impact : Customers using properly signed SigV4 requests will not experience any impact. Customers relying on unsigned x-amz-* headers in their requests may need to update their request signing process to ensure all x-amz-* headers are included in the signature.

This is tracked upstream as OpenStack Swift bug [#2158733]
(https://bugs.launchpad.net/swift/+bug/2158733) affecting Swift ≥ 2.18.0.

What you will see

A request with an unsigned `x-amz-*` header now returns **HTTP 403** with :
```xml

AccessDenied

There were headers present in the request which were not signed
x-amz-storage-class

```
The HeadersNotSigned element names the offending header, so it is easy to diagnose.

Are you affected?

- **Standard SDKs (boto3, AWS SDKs) and the AWS CLI: not affected.** They already sign every `x-amz-*` header they send. Keep your SDK reasonably up to date and you have nothing to do.
- **You may be affected if** you build requests by hand, or generate a presigned URL and then attach extra `x-amz-*` headers (storage class, ACL, tagging, user metadata, copy-source, SDK checksum headers…) at request time.
Those headers must be part of the signature.
- **One exception:** `x-amz-content-sha256` does not need to be listed in `SignedHeaders` its value already *is* the payload hash bound into the signature. (`x-amz-date` **does** have to be signed.)

How to presign requests correctly

The rule is the same in every language: **put the `x-amz-*` headers into the signing step, and make the caller replay exactly those headers** — never add an `x-amz-*` header to an already-generated presigned URL.



Python (boto3)

**Do sign the headers at generation time** — pass them to
`generate_presigned_url` via `Params` so boto3 folds them into the signature:

```python
import boto3

s3 = boto3.client("s3", endpoint_url="https://s3..example.com")

url = s3.generate_presigned_url(
"put_object",
Params={
"Bucket": "my-bucket",
"Key": "my-object",
"StorageClass": "STANDARD_IA", # -> signed x-amz-storage-class
"ACL": "private", # -> signed x-amz-acl
"Metadata": {"team": "backup"}, # -> signed x-amz-meta-team
},
ExpiresIn=3600,
)

# The caller must send EXACTLY those headers with the PUT:
import requests
requests.put(
url,
data=b"...payload...",
headers={
"x-amz-storage-class": "STANDARD_IA",
"x-amz-acl": "private",
"x-amz-meta-team": "backup",
},
)
```

**Do NOT** presign a bare request and bolt an `x-amz-*` header on afterwards:

```python
# WRONG — the header is not covered by the signature -> 403 AccessDenied
url = s3.generate_presigned_url(
"put_object",
Params={"Bucket": "my-bucket", "Key": "my-object"},
ExpiresIn=3600,
)
requests.put(url, data=b"...", headers={"x-amz-storage-class": "STANDARD_IA"})
```

**Direct (non-presigned) calls need no change** — the SDK signs everything for
you:

```python
s3.put_object(
Bucket="my-bucket", Key="my-object", Body=b"...",
StorageClass="STANDARD_IA", Metadata={"team": "backup"},
)
```

Go (aws-sdk-go-v2)

Set the headers on the input so the presigner signs them. The returned
`req.SignedHeader` lists the headers the caller **must** replay verbatim:

```go
import (
"bytes"
"context"
"net/http"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/aws-sdk-go-v2/service/s3/types"
)

presigner := s3.NewPresignClient(client)
req, err := presigner.PresignPutObject(context.TODO(), &s3.PutObjectInput{
Bucket: aws.String("my-bucket"),
Key: aws.String("my-object"),
StorageClass: types.StorageClassStandardIa, // -> signed x-amz-storage-class
ACL: types.ObjectCannedACLPrivate, // -> signed x-amz-acl
Metadata: map[string]string{"team": "backup"}, // -> signed x-amz-meta-team
}, s3.WithPresignExpires(time.Hour))

// Replay req.SignedHeader as-is; do not add or drop any x-amz-* header.
httpReq, _ := http.NewRequest(req.Method, req.URL, bytes.NewReader(payload))
httpReq.Header = req.SignedHeader.Clone()
// ... send httpReq with your HTTP client ...
```

Direct (non-presigned) calls need no change — the SDK signs every `x-amz-*`
header:

```go
_, err := client.PutObject(context.TODO(), &s3.PutObjectInput{
Bucket: aws.String("my-bucket"),
Key: aws.String("my-object"),
Body: bytes.NewReader(payload),
StorageClass: types.StorageClassStandardIa,
Metadata: map[string]string{"team": "backup"},
})
```

JavaScript / TypeScript (aws-sdk-js-v3)

Put the headers on the command; `getSignedUrl` folds them into the signature.
The caller must send exactly those headers:

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

const s3 = new S3Client({ endpoint: "https://s3..example.com" });

const command = new PutObjectCommand({
Bucket: "my-bucket",
Key: "my-object",
StorageClass: "STANDARD_IA", // -> signed x-amz-storage-class
ACL: "private", // -> signed x-amz-acl
Metadata: { team: "backup" }, // -> signed x-amz-meta-team
});

const url = await getSignedUrl(s3, command, { expiresIn: 3600 });

// The caller must send EXACTLY those headers with the PUT:
await fetch(url, {
method: "PUT",
body: payload,
headers: {
"x-amz-storage-class": "STANDARD_IA",
"x-amz-acl": "private",
"x-amz-meta-team": "backup",
},
});
```

Direct (non-presigned) calls need no change — the SDK signs every `x-amz-*`
header:

```javascript
await s3.send(new PutObjectCommand({
Bucket: "my-bucket", Key: "my-object", Body: payload,
StorageClass: "STANDARD_IA", Metadata: { team: "backup" },
}));
```

Java (AWS SDK for Java v2)

Set the headers on the `PutObjectRequest`; the presigner signs them. The caller
must replay `presigned.signedHeaders()` verbatim:

```java
import java.time.Duration;
import java.util.Map;
import software.amazon.awssdk.services.s3.model.ObjectCannedACL;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.StorageClass;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;

try (S3Presigner presigner = S3Presigner.create()) {
PutObjectRequest objectRequest = PutObjectRequest.builder()
.bucket("my-bucket")
.key("my-object")
.storageClass(StorageClass.STANDARD_IA) // -> signed x-amz-storage-class
.acl(ObjectCannedACL.PRIVATE) // -> signed x-amz-acl
.metadata(Map.of("team", "backup")) // -> signed x-amz-meta-team
.build();

PresignedPutObjectRequest presigned = presigner.presignPutObject(
PutObjectPresignRequest.builder()
.signatureDuration(Duration.ofHours(1))
.putObjectRequest(objectRequest)
.build());

// presigned.url() is the URL; the caller MUST send presigned.signedHeaders()
// (x-amz-storage-class, x-amz-acl, x-amz-meta-team, ...) unchanged.
}
```

Direct (non-presigned) calls need no change — the SDK signs every `x-amz-*`
header:

```java
s3.putObject(
PutObjectRequest.builder()
.bucket("my-bucket").key("my-object")
.storageClass(StorageClass.STANDARD_IA)
.metadata(Map.of("team", "backup"))
.build(),
RequestBody.fromBytes(payload));
```

Rust (aws-sdk-s3)

Set the headers on the operation builder before `.presigned(...)`; the caller
must replay `presigned.headers()` verbatim:

```rust
use std::time::Duration;
use aws_sdk_s3::presigning::PresigningConfig;
use aws_sdk_s3::types::{ObjectCannedAcl, StorageClass};

let presigned = client
.put_object()
.bucket("my-bucket")
.key("my-object")
.storage_class(StorageClass::StandardIa) // -> signed x-amz-storage-class
.acl(ObjectCannedAcl::Private) // -> signed x-amz-acl
.metadata("team", "backup") // -> signed x-amz-meta-team
.presigned(PresigningConfig::expires_in(Duration::from_secs(3600))?)
.await?;

// presigned.uri() is the URL; the caller MUST send presigned.headers()
// (x-amz-storage-class, x-amz-acl, x-amz-meta-team, ...) unchanged.
```

Direct (non-presigned) calls need no change — the SDK signs every `x-amz-*`
header:

```rust
client
.put_object()
.bucket("my-bucket")
.key("my-object")
.body(payload.into())
.storage_class(StorageClass::StandardIa)
.metadata("team", "backup")
.send()
.await?;
```

Browser-based POST uploads

Use `generate_presigned_post` (or the equivalent in your SDK) — the fields are covered by the signed policy document, so this pattern is already correct.


Action required

Review any code that (a) hand-crafts SigV4 requests, or (b) adds `x-amz-*`
headers to a presigned URL after it was generated. Move those headers into the
signing step as shown above. If you rely on standard SDKs, no action is needed.

References

- OpenStack Swift bug [#2158733](https://bugs.launchpad.net/swift/+bug/2158733)
(OSSA-2026, CVEs pending)
- AWS S3 documentation: [Signature Calculations for the Authorization Header](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html)

Thank you for your understanding.
Posted Jul 28, 2026 - 16:36 UTC
This scheduled maintenance affected: Storage || Object storage (BHS, GRA, DE, RBX, SBG, SGP, SYD, UK, US-EAST-VA-1, US-WEST-OR-1, WAW, YYZ, LIM, AP-SOUTH-MUM-1, EU-WEST-PAR-A, EU-WEST-PAR-B, EU-WEST-PAR-C, YNM1, EU-SOUTH-MIL-B, EU-SOUTH-MIL-A, EU-SOUTH-MIL-C).