Stay organized with collections
Save and categorize content based on your preferences.
This page provides an overview of signed URLs when working with the V2 signing
process, which is a mechanism for query
string authentication for buckets and objects.
Signed URLs provide a way to give time-limited read or write access to anyone
in possession of the URL, regardless of whether they have a user account.
Components of the string that requires signing
When creating a signed URL using a program, your program constructs a string
that will be signed. This string should be defined in your program as:
The components that make up this structure are described in the following table:
String Component
Example
Description
HTTP_Verb
GET
Required. The HTTP verb to be used with the signed URL.
Note: The HTTP verb POST is not supported in signed URL strings, except as noted above. You can use POST to define signed policy documents, which specify the characteristics of objects that can be uploaded to a bucket. Learn more in the POST Object documentation.
Content_MD5
rmYdCNHKFXam78uCt7xQLw==
Optional. The MD5 digest value in Base64. If you provide this in the string, the client (usually a browser) must provide this HTTP header with this same value in its request.
Content_Type
text/plain
As needed. If you provide a content-type, the client (browser) must provide this HTTP header set to the same value.
Expires
1388534400
Required. This is the timestamp (represented as the number of seconds since the Unix Epoch of 00:00:00 UTC on January 1, 1970) when the signature expires. The server rejects any requests received after this timestamp, as well as any requests received after the key used to generate the signed URL is rotated. For security and for compatibility with the V4 signing process, you should set Expires to correspond to at most 1 week (604800 seconds) in the future.
Canonicalized_Extension_Headers
x-goog-acl:public-read\nx-goog-meta-foo:bar,baz\n
As needed. The server checks to make sure that the client provides matching values in requests using the signed URL. For information about how to create canonical headers for signing, see Canonical extension headers.
Canonicalized_Resource
/bucket/objectname
Required. The resource being addressed in the URL. For more details, see Canonical resources.
Signing strings with the App Engine App Identity service
When creating a signed URL using a program, you can either sign the string
from within your program, or else from
within a App Engine application using the App Engine Identity service,
which uses App Engine's service account credentials. For example, using the
Python App Identity API, you can:
Use google.appengine.api.app_identity.sign_blob() to sign the bytes from
your constructed string, providing the Signature you need when
assembling the signed URL.
Use google.appengine.api.app_identity.get_service_account_name()
to retrieve a service account name, which is the GoogleAccessId you need
when assembling the signed URL.
The App Identity service rotates the private keys when it signs blobs. Signed URLs
generated from the App Identity service are good for at least one hour, and are best used for
short-lived access to resources.
Canonical extension headers
When creating a signed URL using a program, you construct the Canonical
Extension Headers portion of the message by concatenating all
extension (custom) headers that begin with x-goog-. However, you cannot perform a simple
concatenation. Keep the following algorithm in mind as you create the headers:
Make all custom header names lowercase.
Sort all custom headers by header name using a lexicographical sort by code point value.
If present, remove the x-goog-encryption-key and x-goog-encryption-key-sha256
headers. These headers contain sensitive information that must not be included
in the string-to-sign; however, these headers must still be used in any
requests that use the generated signed URL.
Eliminate duplicate header names by creating one header name with a comma-separated list of
values. Be sure there is no whitespace between the values, and be sure that the order of the
comma-separated list matches the order that the headers appear in your request. For more
information, see RFC 7230 section 3.2.
Replace any folding whitespace or newlines (CRLF or LF) with a single space. For more
information about folding whitespace, see
RFC 7230, section 3.2.4..
Remove any whitespace around the colon that appears after the header name.
Append a newline "\n" (U+000A) to each custom header.
Concatenate all custom headers.
Canonical resources
When creating a signed URL using a program, you construct the
Canonicalized Resource portion of the message by concatenating the resource
path (bucket and object and subresource) that the request is acting on. Keep
the following in mind as you create the resource:
The canonical resource is everything that follows the host name. For example,
if the Cloud Storage URL is https://storage.googleapis.com/example-bucket/cat-pics/tabby.jpeg,
then the canonical resource is /example-bucket/cat-pics/tabby.jpeg.
If the request is scoped to a subresource, such as ?cors, add this subresource,
including the question mark, to the end of the string.
Be sure to copy the HTTP request path literally: that is, you should include all URL encoding
(percent signs) in the string that you create. Also, be sure that you include only query string
parameters that designate subresources (such as cors). You should not include query string
parameters such as ?prefix, ?max-keys, ?marker, and ?delimiter.
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Hard to understand","hardToUnderstand","thumb-down"],["Incorrect information or sample code","incorrectInformationOrSampleCode","thumb-down"],["Missing the information/samples I need","missingTheInformationSamplesINeed","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2025-09-04 UTC."],[],[],null,["# V2 Signing Process\n\n| **Important:** This page covers legacy material related to the V2 signing process. It is recommended that users work with the [V4 signing process](/storage/docs/access-control/signed-urls) instead.\n\nThis page provides an overview of signed URLs when working with the V2 signing\nprocess, which is a mechanism for query\nstring authentication for buckets and objects.\nSigned URLs provide a way to give time-limited read or write access to anyone\nin possession of the URL, regardless of whether they have a user account.\n| **Important:** Signed URLs can only be used to access resources in Cloud Storage through [XML API endpoints](/storage/docs/request-endpoints).\n\nComponents of the string that requires signing\n----------------------------------------------\n\nWhen creating a signed URL using a program, your program constructs a string\nthat will be signed. This string should be defined in your program as: \n\n```\nStringToSign = HTTP_Verb + \"\\n\" +\n Content_MD5 + \"\\n\" +\n Content_Type + \"\\n\" +\n Expires + \"\\n\" +\n Canonicalized_Extension_Headers +\n Canonicalized_Resource\n```\n\nThe components that make up this structure are described in the following table:\n\n| **Note:** Query String Parameters like `response-content-disposition` and `response-content-type` are not verified by the signature. To force a `Content-Disposition` or `Content-Type` in the response, [set those parameters in the object metadata](/storage/docs/viewing-editing-metadata#edit).\n\nSigning strings with the App Engine App Identity service\n--------------------------------------------------------\n\nWhen creating a signed URL using a program, you can either sign the string\nfrom within your program, or else from\nwithin a App Engine application using the App Engine Identity service,\nwhich uses App Engine's service account credentials. For example, using the\n[Python App Identity API](/appengine/docs/python/appidentity), you can:\n\n- Use `google.appengine.api.app_identity.sign_blob()` to sign the bytes from\n your constructed string, providing the `Signature` you need when\n assembling the signed URL.\n\n- Use `google.appengine.api.app_identity.get_service_account_name()`\n to retrieve a service account name, which is the `GoogleAccessId` you need\n when assembling the signed URL.\n\nFor support in other languages, see\n[App Identity API for Java Overview](/appengine/docs/java/appidentity),\n[App Identity API for PHP Overview](/appengine/docs/php/appidentity),\nand [App Identity Go Functions](/appengine/docs/go/appidentity).\n\nThe App Identity service rotates the private keys when it signs blobs. Signed URLs\ngenerated from the App Identity service are good for at least one hour, and are best used for\nshort-lived access to resources.\n\nCanonical extension headers\n---------------------------\n\nWhen creating a signed URL using a program, you construct the Canonical\nExtension Headers portion of the message by concatenating all\nextension (custom) headers that begin with `x-goog-`. However, you cannot perform a simple\nconcatenation. Keep the following algorithm in mind as you create the headers:\n\n1. Make all custom header names lowercase.\n\n2. Sort all custom headers by header name using a lexicographical sort by code point value.\n\n3. If present, remove the `x-goog-encryption-key` and `x-goog-encryption-key-sha256`\n headers. These headers contain sensitive information that must not be included\n in the string-to-sign; however, these headers must still be used in any\n requests that use the generated signed URL.\n\n4. Eliminate duplicate header names by creating one header name with a comma-separated list of\n values. Be sure there is no whitespace between the values, and be sure that the order of the\n comma-separated list matches the order that the headers appear in your request. For more\n information, see [RFC 7230 section 3.2](https://datatracker.ietf.org/doc/html/rfc7230#section-3.2).\n\n5. Replace any folding whitespace or newlines (CRLF or LF) with a single space. For more\n information about folding whitespace, see\n [RFC 7230, section 3.2.4.](https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4).\n\n6. Remove any whitespace around the colon that appears after the header name.\n\n7. Append a newline \"\\\\n\" (U+000A) to each custom header.\n\n8. Concatenate all custom headers.\n\n| **Important:** You must use both the header name and the header value when you construct the Canonical Extension Headers portion of the query string. Be sure to remove any whitespace around the colon that separates the header name and value. For example, using the custom header `x-goog-acl: private` without removing the space after the colon returns a `403 Forbidden` error, because the request signature you calculate does not match the signature Cloud Storage calculates.\n\nCanonical resources\n-------------------\n\nWhen creating a signed URL using a program, you construct the\nCanonicalized Resource portion of the message by concatenating the resource\npath (bucket and object and subresource) that the request is acting on. Keep\nthe following in mind as you create the resource:\n\n- The canonical resource is everything that follows the host name. For example,\n if the Cloud Storage URL is `https://storage.googleapis.com/example-bucket/cat-pics/tabby.jpeg`,\n then the canonical resource is `/example-bucket/cat-pics/tabby.jpeg`.\n\n- If the request is scoped to a subresource, such as `?cors`, add this subresource,\n including the question mark, to the end of the string.\n\n- Be sure to copy the HTTP request path literally: that is, you should include all URL encoding\n (percent signs) in the string that you create. Also, be sure that you include only query string\n parameters that designate subresources (such as `cors`). You should not include query string\n parameters such as `?prefix`, `?max-keys`, `?marker`, and `?delimiter`."]]