Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Storage v1.51.0: uploads bypass client-level restOptions (proxy, verify) due to per-call override

Open
#9,212 6 comments 1 reaction 1 assignee View on GitHub

@Hectorhammett is already working on this.

Since May 26, 2026.

Assessment

This issue has not been assessed yet.

Description

Environment
  • google/cloud-storage: v1.51.0
  • google/cloud-core: v1.72.0
  • PHP: 8.x
Summary

Since google/cloud-storage v1.51.0, uploads via Bucket::upload() (and the underlying MultipartUploader / ResumableUploader) no longer honor the restOptions passed to the StorageClient constructor — notably proxy and verify. The change is a regression introduced by #8825 ("Enable full object checksum validation on JSON path").

For environments where outbound HTTPS to storage.googleapis.com is only reachable through an HTTP forward proxy, this breaks all uploads after upgrading from v1.50.x.

Reproduction
use Google\Cloud\Storage\StorageClient;

$storage = new StorageClient([
    'restOptions' => [
        'proxy'  => 'http://proxy.example.com:8080',
        'verify' => false,
    ],
    // ... credentials etc.
]);

$bucket = $storage->bucket('my-bucket');
$bucket->upload(fopen('/tmp/some.csv', 'r'), ['name' => 'foo.csv']);
  • v1.49.x / v1.50.x: the upload request is sent through http://proxy.example.com:8080.
  • v1.51.0: the upload request goes direct, ignoring the proxy and failing in proxy-only environments.

Metadata calls (e.g. $bucket->info(), $bucket->exists()) and auth token fetches still go through the proxy as expected. Only the actual upload POST is affected.

Root cause

#8825 added X-Goog-Hash header injection in Storage/src/Connection/Rest.php::resolveUploadOptions():

if (!empty($xGoogHashHeader)) {
    $args['uploaderOptions']['restOptions']['headers']['X-Goog-Hash'] = $xGoogHashHeader;
}

uploaderOptions is then passed to MultipartUploader / ResumableUploader, stored as $this->requestOptions, and forwarded to RequestWrapper::send($request, $this->requestOptions).

In Core/src/RequestWrapper.php::getRequestOptions():

$restOptions = $options['restOptions'] ?? $this->restOptions;

Per-call $options['restOptions'] replaces the wrapper's stored $this->restOptions instead of merging with it. Before v1.51.0 the uploader didn't pass per-call restOptions (no header injection), so the stored restOptions — including proxy and verify — was used. Now that the uploader always sets restOptions.headers.X-Goog-Hash (because chooseValidationMethod() defaults to crc32 / md5), the stored restOptions is silently dropped on every upload.

Suggested fixes

Either:

  1. In RequestWrapper::getRequestOptions() — merge per-call restOptions with the stored one rather than overriding (deep merge, with per-call values winning on conflict). This is the more general fix and preserves the existing API contract for callers that only want to add headers.
  2. In Rest::resolveUploadOptions() — instead of writing to $args['uploaderOptions']['restOptions']['headers']['X-Goog-Hash'], pass the header through a dedicated headers field that gets merged with the wrapper's existing restOptions at send time.

(1) is preferable since it also covers the same pattern in #9210 (downloads CRC32C validation) before it lands.

Note

The bug appears to extend to #9210 (object download checksum validation, currently open) which uses the same uploaderOptions.restOptions.headers pattern — worth fixing before that PR merges.

Dominant language
PHP
Stars
1.2k
Forks
464
Avg merge
2d 2h
Merged PRs (30d)
103

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from googleapis/google-cloud-php

All issues in googleapis/google-cloud-php

Similar issues

More PHP issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.