Skip to main content

gcs-static-site-proxy

Serve one private Google Cloud Storage static-site prefix, or an explicit catalog of prefixes, through a renewable, read-only local proxy. The GCS bucket remains private: the proxy uses the operator's Application Default Credentials (ADC) and exposes only explicitly configured object prefixes.

Status: alpha. Review the publication-prefix and network-access security model before using the proxy with protected data.

Requirements

  • Python 3.11 or newer
  • uv for the recommended isolated invocation
  • Google Cloud CLI for initializing or refreshing ADC
  • storage.objects.get on the configured objects, normally through roles/storage.objectViewer or a stronger role

Initialize ADC:

gcloud auth login --update-adc

Install and run on localhost

Run the published package in an isolated environment:

uvx gcs-static-site-proxy@0.2.0 \
  --bucket private-site-bucket \
  --prefix team/sites/my-site

Alternatively, install it with pip and use either the command or Python module entry point:

python -m pip install gcs-static-site-proxy==0.2.0
python -m gcs_static_site_proxy \
  --bucket private-site-bucket \
  --prefix team/sites/my-site

Run directly from a pinned public Git commit during development:

uvx --from 'git+https://github.com/alexei-ciobanu/gcs-static-site-proxy@COMMIT_SHA' \
  gcs-static-site-proxy \
  --bucket private-site-bucket \
  --prefix team/sites/my-site

The default listener is 127.0.0.1:8080. It accepts only GET and HEAD, rejects path traversal, and does not provide a general GCS proxy. Outbound authentication and GCS requests use the operating system trust store, including root certificate authorities installed through device management.

Multiple sites and a landing page

Catalog mode serves a landing-page prefix at / and mounts explicitly allowlisted site prefixes under /sites/<slug>/:

uvx gcs-static-site-proxy@0.2.0 \
  --bucket private-site-bucket \
  --catalog-prefix team/publication/static-sites

The catalog prefix must contain this exact object:

team/publication/static-sites/.gcs-static-site-proxy-sites.json

Example:

{
  "version": 1,
  "sites": [
    {
      "slug": "service-review",
      "title": "Service review",
      "prefix": "team/projects/service-review/publication/site"
    }
  ]
}

The landing page remains under the catalog prefix. Each mount maps only to its declared prefix; unknown slugs fail closed, and neither catalog nor site configuration objects are browser-accessible. Catalog editors can expose any prefix that their proxy operator can read, so treat the catalog as a browser publication allowlist and never mount a protected artifact prefix.

The proxy loads and validates the catalog and every site's CSP at startup. The landing page uses the catalog prefix's .gcs-static-site-proxy.json; each mounted site uses the same well-known filename under its own prefix. Explicit CLI CSP overrides apply to all mounts.

Path-mounted sites share a browser origin. Per-response CSPs remain distinct, but a script running in one mounted site can make same-origin requests to other mounted sites. Use one catalog only for sites approved for the same audience; use separate origins when stronger isolation is required.

Site security configuration

Unless overridden, the proxy requests this exact object at startup:

<prefix>/.gcs-static-site-proxy.json

Example:

{
  "version": 1,
  "contentSecurityPolicy": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'",
  "browserCache": "revalidate"
}

If it is absent, a strict built-in policy is used. An invalid or inaccessible configuration fails startup. The active source, generation and SHA-256 are printed.

browserCache is optional and supports no-store (the default) or revalidate. Revalidation keeps HTML, extensionless objects and proxy-controlled responses at private,no-store. Eligible non-HTML objects with filename extensions use private,no-cache; the proxy forwards browser If-None-Match and If-Modified-Since validators to GCS and relays unchanged responses as HTTP 304 without downloading their bodies. If a conditional GCS response omits its content type or identifies its representation as HTML, the proxy retries it unconditionally; HTML is served as private,no-store.

Unlike no-store, private,no-cache permits the browser to retain private object bytes in its local cache. It prevents shared-cache reuse and requires a successful GCS revalidation before normal browser reuse, so revoked or expired credentials are not hidden by a fresh cache entry. Enable it only when local browser storage is suitable for the site's publication classification.

Without an explicit CLI mode, each prefix uses its own GCS configuration and falls back to the strict built-in policy when that object is absent. The mutually exclusive global modes are:

  • --csp-override-file PATH applies one exact local policy to every route;
  • --strict-csp applies the strict built-in policy to every route; and
  • --no-csp disables CSP on every route.

In catalog mode these options affect the landing page and every mounted site. For a local override of only one site, run that prefix separately in single-site mode.

The site's browser-cache setting is also resolved independently for the landing page and each catalog mount. A global override is available for local testing or deliberately uniform catalogs:

--browser-cache revalidate

--no-csp disables only CSP. The proxy continues to send no-referrer, nosniff, and frame-denial headers.

Network access

Loopback is the safe default. Explicit non-loopback binding enables network mode:

uvx gcs-static-site-proxy@0.2.0 \
  --bucket private-site-bucket \
  --prefix team/sites/my-site \
  --bind 0.0.0.0

Network mode generates a process-lifetime access token. Opening the printed tokenized URL exchanges it for an HttpOnly, SameSite session cookie. IP-literal Host values are accepted when they match the local destination address; machine hostnames are detected automatically. Use --allow-host HOST for an additional DNS alias.

The cookie is intentionally a browser-session cookie rather than a persistent credential. If the browser is restarted while the proxy remains running, open the printed tokenized URL again.

Anyone who has the token and can reach the listener can read the site using the operator's GCS permissions. Plain HTTP does not protect content from network interception. Supply both options for HTTPS:

--tls-cert certificate.pem --tls-key private-key.pem

Remote visitors cannot launch the local ADC reauthentication command.

Local development

uv sync --dev
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run ty check src
uv build --no-sources

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gcs_static_site_proxy-0.2.0.tar.gz (118.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gcs_static_site_proxy-0.2.0-py3-none-any.whl (26.0 kB view details)

Uploaded Python 3

File details

Details for the file gcs_static_site_proxy-0.2.0.tar.gz.

File metadata

  • Download URL: gcs_static_site_proxy-0.2.0.tar.gz
  • Upload date:
  • Size: 118.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for gcs_static_site_proxy-0.2.0.tar.gz
Algorithm Hash digest
SHA256 133234c0404a5fe9d75dc4c72d37c0ad799515441701b96748a99fa746308bae
MD5 c2d1ef6d57b5c8f87da4a984c3362894
BLAKE2b-256 7324bbfebb54f80e50a68982eb35c429c72cafe61f479f9d747ae1eea4e4cfe2

See more details on using hashes here.

File details

Details for the file gcs_static_site_proxy-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: gcs_static_site_proxy-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 26.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for gcs_static_site_proxy-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 dd18416a253016371ca6437451c1684cb532a7bdc5ee721207b73398b781d3e2
MD5 7e433bb2b92cbf5f37ebd900d41c5a36
BLAKE2b-256 ed7e4261a2e28f5fa3938f6ae7a5cdaf315700a8ef110f5dcc45ec688c293a4a

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page