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
uvfor the recommended isolated invocation- Google Cloud CLI for initializing or refreshing ADC
storage.objects.geton the configured objects, normally throughroles/storage.objectVieweror 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.1.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.1.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.1.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'"
}
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.
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 PATHapplies one exact local policy to every route;--strict-cspapplies the strict built-in policy to every route; and--no-cspdisables 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.
--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.1.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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gcs_static_site_proxy-0.1.0.tar.gz.
File metadata
- Download URL: gcs_static_site_proxy-0.1.0.tar.gz
- Upload date:
- Size: 114.4 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
651278ec7ec7b486778005b347ca145dc7225cbddf718a3b543b3e5fc983cce3
|
|
| MD5 |
e177cdc3c72d124558a6558c9d1bca4e
|
|
| BLAKE2b-256 |
a76a0d4fcce206925df1c48e1c3f2fb902db51de1f0b2ea83a44789b0d9bc52c
|
File details
Details for the file gcs_static_site_proxy-0.1.0-py3-none-any.whl.
File metadata
- Download URL: gcs_static_site_proxy-0.1.0-py3-none-any.whl
- Upload date:
- Size: 24.3 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cd36a066958ccdf77a685ea7c5d894e3d31f91a5b7adf82f8f63b3a655465627
|
|
| MD5 |
d03afbda9bf4b7039810b7433e803292
|
|
| BLAKE2b-256 |
5983ea2507497d9c68008cb4a5104ab79c7136996150d0c68dd60995a735adb0
|