mediumIntermediateSymptom-first#cloud storage#signed url#expired#signature#gcs

Cloud Storage signed URL expired or invalid

Downloads via signed URL fail with 'The given URL is not valid' or 403

Likely source:securityconfiguration

Focused incident mode — strips away everything but this runbook.

Symptoms

  • Downloads via signed URL fail with 'The given URL is not valid' or 403
  • App-generated links stop working after a time
  • gsutil signurl works but browser download fails
  • Expiration/clock skew errors in the client

Signed URLs are snapshots in time — short TTLs are safer for security but break long-held links. Use short TTLs for credential-like access and a signing helper if clients need to keep a URL for a while.

Evidence to grab

Collect these artifacts before you change anything — they prove (or rule out) each cause.

  • Exact signed-URL error

    curl -i '<SIGNED_URL>'
  • Signing credentials

    gcloud auth list --filter=status:ACTIVE

Diagnostic flow

Run the steps in order, marking each claim confirmed or refuted. The evidence you gather feeds the ranked causes below.

Guided decision tree is ready.

Steps with commands, expected output, and pass/fail branches.

Ranked root causes

Evidence checklist

  • curl error reports an expired signature
  • URL's Expires= value is in the past
  • Links generated with a short TTL broke for long-lived clients

Confirm before fixing

curl -s '<SIGNED_URL>' -o /dev/null -w '%{http_code}'
READ

Confirm the status code

Expected: 403

Fix

gsutil signurl -d 1h <SERVICE_ACCOUNT_KEYFILE> gs://<BUCKET>/<OBJECT>
READ

Generate a fresh signed URL with a longer TTL

Expected: A new valid signed URL

Resolution runbook

Apply from top to bottom after the cause is confirmed. Review every command before running it — risk tags are shown on each.

1
curl -i '<SIGNED_URL>'
READ

Read the exact error body

2
date -u
READ

Compare current time to the URL expiry

3
gsutil signurl -d <DURATION> <SERVICE_ACCOUNT_KEYFILE> gs://<BUCKET>/<OBJECT>
READ

Re-sign with an appropriate TTL

4
curl -I '<NEW_SIGNED_URL>'
READ

Verify the new URL serves the object

Expected: HTTP 200

Prevention

  • Match signed URL TTL to how long the client will actually hold the link.
  • Centralize signing behind a helper so all components share one SA + TTL policy.
  • Set bucket lifecycle rules so objects referenced by long-lived URLs are not prematurely removed.
  • Monitor 403s with the key words expired/signature across your bucket access logs.

Commands marked READ CHANGE DESTRUCTIVE — never run DESTRUCTIVE commands against production data you cannot restore. Copy → verify → execute.