Generating Time‑Limited Signed URLs for Google Cloud Storage Objects
Learn how to create short‑lived, signed URLs for GCS objects using a service account key, with a Java example, limits, and common mistakes.
21 Aug 2025, 00:13 UTC

Overview
A signed URL gives a caller temporary read or write access to a Cloud Storage object without granting any IAM role on the bucket. The URL contains a cryptographic signature that validates the request against the service account used to create it.
How to generate a signed URL (Java example)
The following snippet shows the minimal steps using the Google Cloud Storage client library for Java. Replace the placeholders with your own values.
import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.cloud.storage.BlobInfo;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.StorageOptions;
import com.google.cloud.storage.Storage.SignUrlOption;
import java.io.FileInputStream;
import java.util.concurrent.TimeUnit;
public class SignedUrlExample {
public static void main(String[] args) throws Exception {
// 1. Load service account key (JSON)
ServiceAccountCredentials credentials = ServiceAccountCredentials.fromStream(
new FileInputStream("/path/to/key.json"));
// 2. Create Storage client with those credentials
Storage storage = StorageOptions.newBuilder()
.setCredentials(credentials)
.build()
.getService();
// 3. Define the blob you want to share
BlobInfo blobInfo = BlobInfo.newBuilder(
"my-bucket", "my-object.txt").build();
// 4. Build the signed URL – 15 minutes, V4 signature, GET method
String url = storage.signUrl(
blobInfo,
15, // expiration length
TimeUnit.MINUTES,
SignUrlOption.withV4Signature(),
SignUrlOption.httpMethod(com.google.cloud.storage.HttpMethod.GET))
.toString();
System.out.println("Signed URL: " + url);
}
}
The signUrl method creates a V4 signature by calling iam.serviceAccounts.signBlob behind the scenes, so the service account must have that permission (or a key file that permits signing).
Limits and constraints
- Maximum expiration for a V4 signed URL is 7 days. Requests for longer periods will fail.
- The URL length is limited by the object name and query parameters; extremely long object names may exceed HTTP URL limits.
- If the bucket has uniform bucket‑level access enabled, the service account only needs
iam.serviceAccounts.signBlob(and optionallystorage.objects.getfor read URLs). Legacy object ACLs are not consulted. - For write‑only URLs, use
HttpMethod.PUT(or POST for resumable uploads) and grantstorage.objects.createon the bucket if the caller lacks it.
Common pitfalls
- Forgetting to set the HTTP method – a GET‑signed URL will reject PUT requests and vice‑versa.
- Using an expired or malformed service account key JSON; the signing operation will throw an authentication error.
- Assuming the URL works when uniform bucket‑level access is disabled and the service account lacks legacy object ACLs; the signed URL may still be rejected.
- Exceeding the 7‑day expiration limit; the API returns an error about invalid expiration.
- Not URL‑encoding special characters in the object name when constructing
BlobInfomanually; the client library handles this, but manual string concatenation can break the signature.
Verification steps
After generating the URL, you can confirm it works as expected:
- Run the Java program locally with a valid service account key.
- Copy the printed URL and test with
curl -I <url>to see the HTTP status (should be 200 for a read URL). - Wait until the expiration time passes and repeat the request; you should receive a 403 or 404.
- If testing a write URL, use
curl -X PUT -T <local-file> <url>and verify a 200 OK response.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.