If you run Kafka at scale, you run a schema registry too, and you've probably put some access control in front of it. But can you say, right now, which applications are allowed to change or delete which schemas?
The honest answer usually isn't a clean yes, because it's a choice between architectural compromises: an HTTP proxy you build and maintain, a vendor license and its lock-in, or an access model too coarse to fit.
Why teams run a schema registry
A schema registry solves a coordination problem. Producers and consumers ship on their own schedules, so they need a shared, versioned contract for the shape of each message. Producers register a schema, consumers resolve it by ID, and the registry checks compatibility as schemas evolve, so a change on one side doesn't break the other.
The value is safe evolution of a shared data shape across teams that never talk directly. Everything producing or consuming structured data then depends on it, which is why who can change it matters.
Authentication isn't authorization
Most teams put authentication in front of the registry, and that's worth doing. But authentication answers one question, who are you, not the one that bites in production: what are you allowed to change?
By default, the free Confluent Schema Registry answers the second question with "anything." Its documented default listener is plain HTTP on every interface (listeners=http://0.0.0.0:8081), and per-subject access control isn't in the free core, it requires the commercial Confluent Platform. Confluent's own docs put it plainly:
"Until either ACLs or Role-Based Access Control is also enabled for Schema Registry, any user can create, alter, and delete Schema Registry subjects." — Confluent, Schema Registry Security Plugin
The control you actually want is per-subject: "the payments team can write payments schemas and nothing else."
Every registry guards schemas differently
The realistic ways to control who can change a schema are uneven, and mostly off by default:
| Approach | Per-subject authorization | Default | Managed as | Uses your Kafka identity |
|---|---|---|---|---|
| Confluent Schema Registry, free | none | open, plain HTTP | nothing to manage | no |
| Apicurio (open source) | registry-wide roles, plus creator-owned artifacts; no per-team or per-prefix rules | off | OIDC roles and config | no |
| Karapace (open source) | yes, per subject via regex ACL (read or write) | off | a static auth file | no |
| Confluent RBAC or security plugin | yes, per subject | off, and commercial | MDS or plugin, Enterprise license | Confluent's plane (or Kafka ACLs) |
| Generic HTTP proxy (nginx, gateway) | coarse, path-prefix only | you build and run it | bespoke config, per registry flavor | no, it matches the URL |
| Schema-aware proxy | yes, per subject, prefix and wildcard | drop-in | one policy, no client changes | yes, the accounts you use for Kafka |
An HTTP proxy sees paths, not subjects
Since the registry speaks HTTP, the natural move is a proxy in front, and plenty of teams do it: stand up nginx or an API gateway, keep port 8081 private, and allow only the routes you want, forcing read-only outside CI or blocking writes and deletes. For basic guardrails on one registry, that's cheap and reasonable.
The limit is that a path proxy reasons about URLs, not schemas or identities. Subject names do appear in the path (POST /subjects/{subject}/versions), so you can gate writes by prefix, for example allow POST to /subjects/payments-*. But that approximation leaks in specific ways:
- reads aren't prefix-scoped:
GET /schemas/ids/{id}fetches any schema by numeric ID, with no subject in the path to match on - it authorizes the network path, not your Kafka principal, so it's only as strong as the firewall around 8081
- it assumes subjects are named after topics; with
RecordNameStrategythe subject is a record name, and your prefix rules stop lining up - it can't tell an intended registration from a client silently auto-registering on first produce (
auto.register.schemas=true), so you disable that separately
You can paper over each gap with more config and glue code, but at that point you're rebuilding, and then maintaining, the registry's own model of subjects and owners inside your proxy.
The fix: a schema-aware proxy
There are three ways to add per-subject authorization to a schema registry, and two of them leave you paying more or maintaining custom infrastructure:
- Confluent RBAC can do it, but you'll need to license the broader platform and accept the lock-in.
- A generic HTTP proxy can restrict routes, but it doesn't understand schema subjects. Making it subject-aware means building, maintaining, and supporting custom infrastructure.
- A schema-aware proxy gives you per-subject control using the Kafka identities you already have, without another license or bespoke plumbing.
That's why we built the Conduktor Schema Registry Proxy. It sits in front of the registry you already run, checks read and write permissions for every subject and request, and logs every operation and denial. You don't need to change your producers or consumers. Just point schema.registry.url to the proxy, and keep using your current registry, whether it's Confluent or open source, licensed or free.
Schema access should be controlled by subject, tied to the identities you already use for Kafka, and logged end to end. It shouldn't require a second access system or custom infrastructure your team has to own forever.
