Skip to content

feat: add NEXT_PUBLIC_API_PREFIX for reverse-proxy support#109

Open
thijs-s wants to merge 1 commit intorustfs:mainfrom
ilum-cloud:feat/api-prefix-reverse-proxy
Open

feat: add NEXT_PUBLIC_API_PREFIX for reverse-proxy support#109
thijs-s wants to merge 1 commit intorustfs:mainfrom
ilum-cloud:feat/api-prefix-reverse-proxy

Conversation

@thijs-s
Copy link
Copy Markdown

@thijs-s thijs-s commented May 1, 2026

Pull Request

Description

Adds a build-time env var NEXT_PUBLIC_API_PREFIX that lets the console operate behind a reverse proxy at a path prefix (e.g. /rustfs/api). When set:

  • siteConfig.api.baseURL (in lib/config.ts and lib/config-helpers.ts)
    is prefixed, so the custom AwsClient sends admin requests to
    ${origin}${API_PREFIX}/rustfs/admin/v3.
  • The wire URL of every AWS SDK request gets the prefix added on the wire
    by a finalizeRequest middleware in a new lib/api-prefix-middleware.ts,
    registered in S3Provider (contexts/s3-context.tsx) and the STS client
    factory (lib/sts.ts). The middleware runs after the SDK's auth
    middleware signs the request, so the signature is computed against the
    un-prefixed canonical URI.
  • The custom SigV4 signer in lib/aws4fetch.ts strips the prefix from
    this.url.pathname before it builds this.encodedPath (the
    canonical URI used in the signature). The wire request still carries
    the prefix; only the canonical-string-being-signed has it stripped.

A reverse proxy that strips the prefix before forwarding to rustfs
(e.g. nginx rewrite ^/rustfs/api/(.*) /$1 break;) forwards a request
whose signature matches what an unmodified rustfs server verifies. No
server-side change is required.
Default is empty, so existing
deployments are unaffected.

Also refactored module-level API_PREFIX constants in three files into
getApiPrefix() accessor functions so process.env is read on each
call rather than at module load — cleaner for tests and zero runtime
cost.

Related: see #108 for the full motivation, alternatives, and
SigV4 contract diagram. The patch was validated end-to-end against a
real rustfs 1.0.0-alpha.99 deployed behind nginx with the rewrite
rule above (login + STS AssumeRole + bucket list + path-style
object operations + multipart upload all succeed).

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • Test improvements
  • Security fix

Testing

Three new test files under tests/lib/, written in the same node:test
style as bucket-cors.test.ts and error-handler.test.ts:

  • tests/lib/api-prefix-middleware.test.ts — middleware registers
    relation: "after" / toMiddleware: "awsAuthMiddleware", prepends the
    prefix to request.path, normalizes /, no-ops when env is empty,
    is idempotent on re-invocation. (5 tests)
  • tests/lib/aws4fetch.prefix.test.tsAwsV4Signer.encodedPath is
    stripped of the prefix when set (root, path-style bucket+key);
    unchanged when empty (backward-compat) or when path doesn't start
    with the prefix; this.url.pathname (the wire URL) stays untouched;
    trailing slash on the prefix is normalized. (6 tests)
  • tests/lib/config.prefix.test.tsloadRuntimeConfig and
    createDefaultConfig compose api.baseURL with the prefix and leave
    s3.endpoint clean; backward-compat path unchanged when empty;
    trailing slash normalized. (5 tests)

Plus a manual end-to-end run against a real rustfs 1.0.0-alpha.99
behind nginx (login flow / ListBuckets / ListObjectsV2 /
CreateMultipartUpload + UploadPart + CompleteMultipartUpload all
succeed; SigV4 signatures verify on the server side).

  • Unit tests added/updated
  • Manual testing completed

Allow the console to be deployed behind a reverse proxy at a path prefix
(e.g. /rustfs/api) by adding a NEXT_PUBLIC_API_PREFIX build-time env var.

When set, the prefix is added to:
- siteConfig.api.baseURL (lib/config.ts, lib/config-helpers.ts) so the
  custom AwsClient sends admin requests to the prefixed origin.
- The wire URL of every AWS SDK request via a finalizeRequest middleware
  in lib/api-prefix-middleware.ts, registered in S3Provider and STS.

The SigV4 signer (lib/aws4fetch.ts) strips the prefix from the canonical
URI BEFORE computing the canonical string. The wire request keeps the
prefix; the signature is computed against the un-prefixed path. A
reverse proxy strips the prefix before forwarding to rustfs, which then
verifies the signature against the same un-prefixed path. No server-side
change is required.

Defaults to empty (backward-compatible). Documented in .env.example.

Also refactored module-level API_PREFIX constants into getApiPrefix()
accessor functions in the three relevant files so process.env stubbing
in tests is straightforward.

Tests:
- tests/lib/api-prefix-middleware.test.ts
- tests/lib/aws4fetch.prefix.test.ts
- tests/lib/config.prefix.test.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant