Why the Backstage proxy plugin needs a security review before production
A proxy route in Backstage is not a per-user integration. It is a single, authenticated pass-through that a portal team adds so plugins can reach an internal or SaaS API, and when the route injects a stored token, every signed-in user who can reach that route calls the upstream API with the same credential. That is the core risk this page addresses: the proxy turns one stored token into access for everyone who can sign in to the portal, and the upstream system usually cannot tell those users apart.
Many portal teams add proxy routes early, when a single plugin needs data from a ticketing system, a cloud API or an internal service, and the configuration works on the first try. The review that matters happens before production. Ask who can reach each route, which HTTP methods and paths it forwards, what the token can do in the upstream system, and whether the upstream audit log can attribute a call to the Backstage user who made it. This page covers how to restrict those routes with allowedMethods and path limits, how to scope injected tokens, and how to move integrations into dedicated backend plugins that check permissions themselves. It does not cover sign-in provider setup, URL reader allow-lists or template security, which have their own pages in this group.
Before you change anything, confirm the facts for your own deployment: the Backstage version and release line you run, the proxy configuration in your app-config files, and the scopes on each stored token. Check the upstream vendor documentation for whether its tokens can be restricted to read-only or per-resource access, because Backstage cannot narrow a credential that the upstream system issues broadly. The Backstage authentication documentation explains how signed-in identities reach the backend, which is the starting point for deciding who should be able to use each route.
Why injected credentials reach every user who can call the proxy
The Backstage proxy authenticates each user to Backstage, but it authenticates Backstage to the upstream API with one credential that the operator configured. Backstage's permission checks run inside the portal, so they do not follow the request once it leaves. The upstream service sees only the injected token, which means any signed-in user who can reach the proxy route is effectively acting with that token's authority. Treat the proxy as a way to hand upstream access to every portal user, and review it that way before enabling it.
The first consequence is scope. A user can call any path and method that the injected token allows, not just the calls a portal page would make. A token that can read one project will still let a crafted request read anything else that token can read, and a token with write or administrative rights turns an ordinary portal account into an upstream administrator. Ask the owner of each upstream system which permissions the token holds, and whether that set matches what the least privileged portal user needs. Confirm also whether the upstream API offers narrower token types or fine-grained permissions for this purpose.
The second consequence is accountability. Upstream audit logs record the token's identity, not the Backstage user who made the call, so an action taken through the proxy looks like a service account action. Investigators cannot tell which person changed a resource, and access reviews cannot attribute upstream activity to individuals. Before go-live, decide how you will correlate proxy requests with upstream events, for example through the Backstage logs and the request timestamps, and confirm that the upstream system records the request source. Where that correlation is not possible, move the integration to a design where the portal calls the upstream API itself under the user's identity or under a narrowly scoped service identity.
Which integrations can stay on the proxy and which need a backend plugin
Read-only status checks and catalog-style lookups carry the lowest risk when routed through the proxy, because the worst outcome of a misuse is disclosure of data that the injected credential can already read. A proxy route is usually acceptable here if the upstream credential is a read-only service account and the route is restricted to the specific GET paths the UI needs. The risk grows quickly when the same route also exposes list endpoints that return more data than a portal page needs.
- Method limits: Allow only
GET(andHEADif your version needs it) through the route, and confirm the exactallowedMethodssetting for your version in the Backstage proxy documentation. - Path limits: Pin each route to the endpoints the portal actually calls, not a whole API prefix.
- Credential scope: Use a read-only token with the narrowest scopes the upstream system offers, never an administrator token.
- Response content: Check whether the upstream response includes fields, such as personal data or internal hostnames, that every signed-in user should not see.
- Caching: Confirm whether responses are cached and who can see cached results.
Before accepting a read-only proxy route, confirm the upstream token cannot read more than the portal's pages display, and test the route with a method other than GET to verify it is refused.
Write actions such as triggering a pipeline, creating a ticket or changing a repository setting change state in another system. A proxy route can forward these, but every signed-in user then acts through one shared credential, so the upstream audit trail records the service account rather than the person. Authorization decisions also move out of Backstage's permission framework and into whatever the proxy path allows. For most write actions a dedicated backend plugin or a scaffolder action is the safer design.
- Shared identity: A proxy write lets any user who can reach the route act as the service account, which weakens attribution.
- Input validation: Proxy forwarding passes request bodies through with little checking, so a backend plugin can validate parameters and reject unexpected fields.
- Permission checks: A backend plugin can call the permission framework before each action, while a proxy route checks only the request path and method.
- Idempotency and errors: Write calls need defined retry and failure behavior that a generic forwarder does not provide.
- Audit events: A plugin can emit its own audit records for each action, which is easier to route to a SIEM than upstream logs alone.
If a proxy write is unavoidable, confirm with the upstream vendor which token types can perform the action and whether the upstream system can record the requesting user.
Some calls should succeed only when the signed-in user has the right to perform them in the upstream system, such as viewing private repositories or seeing projects they belong to. A shared proxy credential cannot express this, because the upstream system sees one identity for everyone. A dedicated backend plugin that resolves the user's identity and makes calls on their behalf, or that exchanges the user's token for a scoped one, keeps the upstream permission model intact. This is the usual case for a backend plugin.
- Identity resolution: The backend plugin should map the Backstage user to the upstream account through a known identifier, and fail closed when no mapping exists.
- Token handling: Confirm whether the sign-in provider returns an access token that Backstage can use, and whether it can be refreshed and stored safely.
- Scope minimization: Request only the scopes the feature needs, and document them for the security team.
- Failure behavior: Users without a matching upstream account should see a clear denial, not a fallback to a shared credential.
- Testing: Verify with at least two users with different upstream rights that results differ as expected.
Before choosing a design, confirm which sign-in provider supplies the upstream token and what your version's authentication documentation says about using it.
A practical decision uses three questions in order: what the call changes, whose rights it needs, and how much an unexpected response could expose. If the call is read-only, uses a service account that holds only reading rights, and returns data every portal user may see, a tightly limited proxy route is a reasonable choice. If the call changes state or depends on who the user is in the upstream system, a dedicated backend plugin is the safer design. Mixed integrations often split into a proxy route for status and a backend plugin for actions.
- Classify each call: List every upstream endpoint the portal uses and tag it as read, write or user-delegated.
- Record the owner: Name the team that approves each proxy route and each plugin.
- Document credentials: Note which token each route uses, its scopes and its rotation method.
- Review on change: Re-check the classification whenever a new endpoint or feature is added.
- Plan the migration: Moving an existing proxy integration into a plugin needs a cutover plan and a rollback path.
Before finalizing the split, confirm the exact proxy and plugin configuration keys in the Backstage documentation for your installed version.
Proxy controls to review: allowed methods, path limits and token scope
This table compares the controls you can apply to each proxy route, from HTTP methods and path prefixes to header handling, token storage and who may call the route. Read each row against the risk it reduces, then check the typical setting for a read-only integration against a write integration. Confirm each setting against the Backstage version you run, because proxy configuration keys and defaults change between releases.
| Control | Risk it reduces | Read-only vs write integration | What to confirm in your version |
|---|---|---|---|
| Allowed HTTP methods | Any signed-in user sending a POST, PUT, PATCH or DELETE through the proxy to change data in the upstream system | Read-only: GET only. Write: list the specific methods the plugin needs, reviewed per route | Confirm the proxy config key that restricts methods and that it applies to every route, not only the default |
| Allowed path prefixes | Reaching admin, export or user-management endpoints on the upstream API that share a host with the approved endpoints | Read-only: narrow prefixes to the listed resources. Write: one prefix per resource the plugin edits | Confirm whether path matching is prefix-based or exact, and test that a sibling path is refused |
| Headers forwarded from the caller | Caller-supplied headers (such as Authorization or internal routing headers) reaching the upstream and overriding injected credentials | Read-only: forward only Accept and content negotiation headers. Write: forward only what the upstream requires | Confirm which headers the proxy strips by default and whether your config adds any override |
| Headers injected by the proxy | Injected credentials visible to every route user, or sent to hosts that were not meant to receive them | Read-only: one scoped read token per route. Write: a separate token with write scope, never shared with read routes | Confirm the target host is pinned in config so the token cannot be sent to another origin |
| Token storage | Tokens committed to app-config files or readable from the frontend bundle | Same for both: reference the token from an environment variable or secrets manager, never a literal value | Confirm how your version substitutes secrets into config and that the value is not echoed in logs |
| Token scope and lifetime | A broad, long-lived upstream credential being used for every user's request | Read-only: a token limited to read permissions on the needed resources. Write: a narrower write-scoped token with a rotation owner | Ask the upstream vendor which scopes and expiry options exist and record who rotates the token |
| Who may call the route | Any authenticated user reaching an upstream with the portal's credential | Read-only: all members of a catalog group that needs the data. Write: a named group only, enforced by the backend | Confirm whether the route checks user identity or only that a session exists, and test with a user outside the group |
| Logging of proxied requests | Inability to trace which user changed or read upstream data through the portal | Read-only: log method, path and user identity. Write: log the same plus the response status, shipped to your SIEM | Confirm that request bodies and injected headers are excluded from logs |
How to lock down an existing proxy route step by step
This sequence takes a proxy route that forwards every request with injected upstream credentials and reduces it to the methods, paths and scoped tokens that real consumers need. Each step ends with a check so you can confirm the route is narrower before you move on.
Inventory every proxy route and its upstream
List each entry under the proxy plugin configuration in app-config, with its mount path, target URL and any header or token it injects. Include routes added by teams that no longer maintain them, since those are the ones most likely to forward credentials to an API nobody watches. Confirm the list against the running configuration in each environment, not only the files in the repository. The output is a table with one row per route, naming the owning team and the upstream system it reaches.
Record the methods and paths consumers actually use
Pull access logs from the Backstage backend and from the upstream API for a representative period, and group the requests for each route by HTTP method and path prefix. Ask the owning plugin or frontend team to confirm any call that appears rarely, such as a quarterly report or an administrative action, so it is not dropped by mistake. Done means every observed method and path prefix is written next to its route, and each one has a named consumer who agreed to it.
Reduce allowedMethods to the observed set
Set the allowedMethods list on each route to only the methods recorded in the previous step, so a route used only for reads no longer accepts writes. Remove any method that exists only because it was the default. Restart the backend in a non-production environment and run the consuming features. The check is that a denied method, sent with a user session that is otherwise valid, returns an error from the proxy and never reaches the upstream API.
Restrict paths to the prefixes in use
Narrow each route so that only the path prefixes from the inventory are forwarded, rather than the whole upstream API under the mount point. Test a path outside the allowed set, such as an administrative endpoint on the same host, and confirm the proxy rejects it. Then check the upstream access log to verify that the rejected request never arrived. Record the final path list in the route's configuration comment so reviewers can compare it with future changes.
Replace broad upstream tokens with scoped tokens
Ask the upstream system's administrators to issue a token limited to the permissions the consuming features need, and retire the broad token that the proxy currently injects. Where the upstream supports it, use a token tied to a service account with a documented owner and an expiry or rotation process. Confirm with the upstream vendor which token types and permission scopes it supports, because capabilities differ by product. The check is that the new token can complete each recorded call and fails on the restricted operations.
Move each token into a secret store
Remove the token value from app-config files and reference it through an environment variable or an external secret reference, sourced from a Kubernetes Secret, a SealedSecret or a secrets manager your organization already runs. Make sure the secret is not written to logs or returned by any endpoint. Verify with a search of the rendered configuration in the running backend that the token value no longer appears in plain text. Document who can read the secret and how it is rotated.
Test each route with a user who should be denied
Sign in as a user without the relevant role, or with a signed-in account from a different group, and attempt each allowed and denied method and path. Expect the allowed calls to succeed and the denied ones to fail at the Backstage proxy. Then check the upstream audit log to confirm that requests carry the expected service identity and that the Backstage user is recorded where the upstream supports it. Keep the test results with the route inventory so you can repeat the same checks after each upgrade or configuration change.
Risks that remain after the proxy route is locked down
Restricting methods and paths narrows what a proxy route can do, but it does not remove everything. The risks below sit alongside a locked-down route, and each one needs a named control and an owner outside the portal team's daily work.
Tokens leaking through logs and errors
The injected credential can appear in request logs, proxy debug output, upstream error bodies or stack traces that Backstage forwards to the browser. Anyone with access to those logs can then call the upstream API as the portal. Check that the proxy and backend logging configuration excludes authorization headers, that error responses are sanitized, and that log access is limited to the platform team. The platform security team should own log redaction and log access reviews.
Upstream APIs broader than the route
The proxy route may expose only a few methods and paths, but the upstream API may accept a much wider set of actions for the same token. A write-capable token behind a read-only route is still one misconfiguration away from a write. Compare the token's granted scopes against the methods the route actually needs, and ask the upstream owner to confirm which operations the credential can perform. The integration owner for each upstream should own this comparison.
Shared service identities blur accountability
When every user's request reaches the upstream under one shared service account, the upstream audit trail records the portal, not the person. Investigations then cannot tie an action to a user, and access reviews cannot test who should still have it. Where the upstream supports it, prefer per-user tokens or identity-aware delegation, and record the Backstage user identity in any request the proxy forwards. The identity and access team should own the decision on which identity the upstream sees.
Stale tokens that never rotate
Integration tokens set once during installation often outlive the people who created them. Without a rotation schedule, a token exposed in a past incident or a departed employee's configuration stays valid indefinitely. Record each token's owner, upstream, scope and expiry in an inventory, and confirm with each upstream how revocation works before you depend on it. The platform operations team should own the rotation schedule and the inventory.
Plugins calling the proxy from the frontend
A frontend plugin that calls the proxy directly can make requests with whatever the browser holds, and anything in the frontend bundle is visible to every signed-in user. This moves the trust boundary into code that users can inspect and modify. Review each frontend plugin for proxy calls, and move calls that need upstream data into a backend plugin that enforces its own checks. The Backstage plugin maintainers should own this review as part of each plugin change.
Proxy routes added without review
Any team that edits app-config can add a proxy route, and a single new entry can expose an upstream path that the security review never saw. Treat proxy configuration like code: changes go through a pull request, a named reviewer checks the allowed methods, paths and injected credentials, and the catalog records which team owns each route. The Backstage portal owners should own the approval step, with the security team as a required reviewer for new credentials.
Moving integrations into dedicated backend plugins with their own permissions
The durable fix for a risky proxy route is to stop routing the integration through the proxy at all and move it into a dedicated backend plugin that owns its upstream client. A custom backend plugin holds the upstream credential in its own configuration, creates its own HTTP client, and exposes a small set of named operations to the frontend or to other backend plugins. The portal no longer forwards arbitrary requests to the upstream API, so the set of operations an attacker can reach is defined by code you review, not by whatever path and method the proxy happens to allow. Each plugin reads only the secret it needs, which keeps a compromise of one integration from exposing the tokens of the others.
The plugin also enforces its own input rules. Where the proxy can only check paths and methods, a plugin can validate the shape of each request, reject identifiers it does not expect, and cap result sizes before calling upstream. Audit trails improve as well, because the plugin logs business operations such as listing a project or triggering a pipeline, instead of an opaque pass-through call. Plugins also shrink the number of shared tokens: a single proxy token used by every route becomes one scoped credential per integration, which is easier to rotate and revoke.
The trade-offs are real. A dedicated plugin is more code to write, test and maintain, and it must be upgraded whenever Backstage changes the plugin API or the upstream vendor changes its endpoints. Confirm which plugin API your Backstage version uses, since the backend system and service-to-service auth interfaces have changed across releases, and check the Backstage authentication documentation for how the plugin should accept calls from the frontend and from other backend plugins. Calls from the plugin upstream should carry a service identity or a user token the plugin is allowed to exchange, not a shared admin credential. Plan who maintains each plugin after the first release, and budget that upkeep as recurring work.
Limits to confirm in your environment before changing the proxy
The proxy plugin sits between your users and upstream APIs, so a configuration change touches every caller at once. The items below are questions for the owner of each system to answer before the design is settled, not conclusions to assume from the documentation alone.
Which proxy keys does your version support
Confirm the exact proxy configuration keys, including any allow-list and method-restriction options, in the Backstage version you run today and in the version you plan to upgrade to. Read them from the plugin documentation and your installed packages, not from older blog posts or examples. Ask whether any key you rely on changed between releases, because a key that is silently ignored leaves the proxy wider than the config file suggests. Record the version you tested against.
Does the upstream API accept scoped tokens
Ask the upstream API owner whether it issues tokens with fine-grained scopes, such as read-only access to specific projects or endpoints, or whether every token grants broad access. If the upstream only offers one broad credential, narrowing the proxy path will not reduce what that credential can do. Document the scopes each integration needs, then check that the upstream enforces them rather than accepting any scope string.
How upstream audit logs record the caller
Find out what the upstream audit log records when a request arrives with a shared service token injected by Backstage. In many setups it records only the token owner, which hides which portal user made the call. Ask whether the log can carry a per-user identity or a request header from the portal, and whether your SIEM can join that record with Backstage audit events before you rely on it for investigations.
Which plugins depend on broad proxy access
List every plugin and custom route that calls an upstream through the proxy today. Ask each owner which paths and methods it uses, and whether it reads responses that a narrower allow-list would block. A plugin that quietly depends on an unlisted endpoint will break when the path limits are tightened, so this inventory decides how much of the change can ship in one step.
Effect on users with active sessions
Ask how a change to proxy configuration or credentials reaches users who are already signed in. Confirm whether the Backstage backend must restart, whether cached responses or sessions keep old behavior for a period, and whether a rotated upstream token invalidates in-flight requests. Plan the change window and tell users which pages may fail briefly, because a partial rollout can leave some users on the old path.
Whether vendor terms allow the intended scope
Check the upstream vendor's terms of service and API usage policy before you issue a narrower or dedicated service identity. Some vendors restrict how service accounts may be created, limit the number of tokens per account, or require a specific account type for automation. Ask your legal or procurement contact to confirm that the scope and identity model you intend are permitted for your contract tier.
How much work is it to secure proxy routes, and what do we need to provide
How much effort does a Backstage proxy security review take?
The effort depends mostly on how many proxy routes exist, which upstream APIs they reach, and whether any route allows write actions. A portal with a handful of read-only routes to one API is a small review. A portal that forwards credentials to several upstream systems, with POST, PUT and DELETE methods enabled and custom plugins that add their own forwarding, takes considerably more time because each route needs its methods, paths and injected credentials checked against what the upstream API actually permits. Ask any partner to base the quote on a written route inventory, and ask how they will test each route with the credential it receives. Effort is not a fixed figure until that inventory exists.
Can our internal team secure the proxy routes without outside help?
Often yes, if the team has someone who knows the upstream APIs well and can read the Backstage proxy configuration. The work needs three skill sets: understanding of Backstage configuration and the proxy plugin, familiarity with the authorization model of each upstream API so the team can judge which methods and paths are safe to expose, and enough testing practice to confirm that a request outside the allowed path or method is refused. Teams without an API security background often find the upstream permission review the slowest part. An outside reviewer adds most value when the team has no one who can answer that second question for every upstream system.
What do we need to provide before a proxy review starts?
The review needs a few inputs from your side before any testing starts:
- A list of every proxy route in the app configuration, with the upstream host, allowed methods and path limits for each.
- A named owner for each upstream API who can confirm which operations the injected credential should be able to perform.
- Test accounts or test tokens in a non-production environment, with the same scopes as production credentials but no real customer data.
- Change windows agreed with the platform and upstream teams, because some fixes will change how existing automation calls those APIs.
Also confirm who approves a change to a credential's scope. Without these inputs, the reviewer has to guess at intended behavior, which lengthens the work.
Does moving integrations into a dedicated backend plugin change cost or timing?
Yes, it usually adds work up front and reduces it later. Replacing a proxy route with a dedicated backend plugin means writing or adapting code, adding tests, and changing the frontend calls that use the route, so the first pass costs more than tightening configuration. The benefit is that each integration gets its own permission checks, scoped credentials and logging, which makes later reviews smaller. Ask a partner to estimate the migration separately from the hardening, and to say which routes they would move, which they would only restrict, and how they would avoid breaking users during the switch.
How does a proxy review relate to a full Backstage threat model review?
A proxy review covers one risk area: credentials injected by Backstage that upstream APIs trust, and the routes that carry them. A broader threat model review also looks at sign-in, URL readers, scaffolder actions, frontend exposure, secrets handling and catalog registration. Proxy findings often overlap with those areas, for example when a proxied API also appears in a template action. If you plan a broader review, scope the proxy work inside it so the route inventory and upstream owners are gathered once. Running them separately is possible but usually repeats the same discovery work.
What does a fixed-scope proxy hardening engagement typically cover?
A fixed-scope engagement sets out, in writing before work starts, the routes in scope, the upstream APIs involved, the changes to make and the tests that prove them. A typical scope for this topic includes reviewing each route's allowed methods and path limits, restricting them to what each integration needs, scoping or replacing the injected credentials, testing refused requests, and documenting the result for the platform team. Items outside the agreed routes, such as unrelated plugins or upgrades, need their own scope. DeployOpen plans, implements and tests this work inside your infrastructure, and does not issue audit reports or attestations. Ask for the scope, deliverables and acceptance tests to appear in the written quote.
Which costs recur after the proxy routes are secured?
Most of the cost is one-time: the inventory, the restrictions, the credential changes and the initial tests. Recurring costs come from new routes added by developers, upstream API changes that alter permitted methods or paths, credential rotation, and periodic re-testing after Backstage upgrades. Ask whether the quote covers a review gate for new proxy entries and who in your organization owns that gate. If you want ongoing testing or upgrade support, DeployOpen offers retainers, and those would be priced separately from the one-time scope.
How DeployOpen scopes and delivers proxy and credential hardening
DeployOpen plans, implements, hardens and documents self-hosted open-source platforms inside infrastructure you control. For Backstage, we treat the proxy plugin and every upstream credential as part of the portal's attack surface, and we agree the scope in writing before any configuration changes begin.
How we scope
- We inventory every proxy endpoint, the upstream APIs it reaches, and the credentials currently injected for them.
- We record which user groups can reach each route and which methods and paths must be allowed or blocked.
- Engagements are fixed-scope, with deliverables and timelines agreed in writing. Ongoing support is available as a retainer.
How we build
- We restrict proxy routes with allowedMethods and path limits, then test that disallowed requests are rejected.
- We move integrations that need upstream credentials into dedicated backend plugins with their own scoped tokens.
- We test token scope, ownership and failure behavior against the upstream systems in a staging environment you provide.
How we hand over
- You receive the proxy configuration, a route inventory, and a record of each credential's owner, scope and storage location.
- We document the tests we ran, the results, and the remaining risks that need an owner on your side.
- We walk your platform and security teams through the changes and the steps to add or review a new proxy route.
How we support
- Retainers cover reviews of new proxy routes and plugin changes as your Backstage deployment evolves.
- We help you track Backstage releases and apply patches to the app and to integration plugins.
- We can rerun the route and credential checks after upgrades, so your evidence stays current with the platform.

