How to generate and configure the JWT secret for ONLYOFFICE DocumentServer
ONLYOFFICE DocumentServer adds a JSON Web Token to every API call between the
editors (browser) and the backend services, signed by a shared secret. The
token prevents an unauthenticated browser from directly talking to your
editors and forcing them to render arbitrary documents. Since v7.2 JWT
is enabled by default and the secret is generated automatically on first
boot — meaning out-of-the-box you don’t need to do anything. The two cases
where you must touch this are: integrating DocumentServer with an external
service (Nextcloud, WordPress, Confluence, your own app), or running it
behind a load balancer that needs to know the secret ahead of time.
This is the full walk-through for a 2026 install on Linux + Docker: how to
read the current secret, change it to your own, and avoid the two traps
that account for 90% of “DocumentServer rejected my JWT” threads.
Where the secret lives
For a native Linux install (.deb / .rpm):
/etc/onlyoffice/documentserver/local.jsonFor a Docker install: there is no file by default. The secret is generated
on first boot and held in container memory + the named volume you mount.
You set your own via the JWT_SECRET environment variable when the
container is started.
In both cases the secret lives in the same JSON path:
{
"services": {
"CoAuthoring": {
"token": {
"enable": {
"browser": true,
"request": {
"inbox": true,
"outbox": true
}
}
},
"secret": {
"inbox": { "string": "YOUR_SECRET_HERE" },
"outbox": { "string": "YOUR_SECRET_HERE" },
"session": { "string": "YOUR_SECRET_HERE" }
}
}
}
}Three things to notice:
- The secret must be identical in
inbox,outbox, andsession.
This is the single most common reason JWT auth fails — an admin copies
the secret file from one server, pastes into another, and forgets the
third section. - Token verification is on by default since 7.2. Pre-7.2 you also have
to flip all threeenable.*flags fromfalsetotrue. If you
upgrade from an old version, manually configured secrets are preserved
but the enable flags stay where you left them. - The HTTP header carrying the token is
Authorization: Bearer ***
(default), with the JWT being standard HS256.
Changing the secret — Linux native
sudo nano /etc/onlyoffice/documentserver/local.json
# replace YOUR_SECRET_HERE in three places — must be the same string
sudo systemctl restart ds-converter ds-docservice ds-metrics
# Confirm with:
sudo cat /etc/onlyoffice/documentserver/local.json | grep '"string"'
# You should see YOUR_SECRET_HERE three times in a row.The restart order matters: ds-converter (handles file conversion to
the browser-friendly formats), then ds-docservice (the actual editor
service), then ds-metrics (Prometheus exporter). Restart them in that
order so old token sessions die cleanly before the new secret gets
loaded.
Changing the secret — Docker
For Docker, never edit local.json inside the container. The
container’s filesystem is ephemeral and any manual change gets nuked on
restart. Use environment variables:
docker run -i -t -d -p 80:80
-e JWT_ENABLED=true
-e JWT_SECRET='YOUR_SECRET_HERE'
-e JWT_HEADER=Authorization
-e JWT_IN_BODY=false
onlyoffice/documentserverNotes:
JWT_ENABLED=falsedisables validation entirely. Don’t use this in
production — it’s debug-only.JWT_HEADER=AuthorizationJwt(the older convention) is still supported;
the default isAuthorizationsince v6.x.JWT_IN_BODY=falsemeans the token is carried in the
Authorizationheader. Iftrue, ONLYOFFICE expects the token in the
request body. Default isfalse.JWT_SECRETis read once at startup. You must recreate the container
to change the secret.docker restartwon’t pick it up.
After changing JWT_SECRET, also update the matching secret in the
connecting service (Nextcloud’s ONLYOFFICE connector, your app’s
config, whatever).
Generating a good secret
The default generation uses a 64-char random hex string. If you set one
yourself, use something equivalently un-guessable. Don’t use a UUID,
don’t use your team’s slack channel name. openssl rand -hex 32 is more
than enough:
openssl rand -hex 32
# → 7c2c89ff04e3... (64-char hex string)The secret just has to be identical on both sides (DocumentServer +
connector); no other characters have meaning. Picking a 64-char hex avoids
all “your string contains a $ and bash ate it” edge cases.
Reading your current secret
If you forgot what you set it to:
# Linux native
cat /etc/onlyoffice/documentserver/local.json |
jq -r '.services.CoAuthoring.secret | to_entries[] | .value.string'
# Docker (without restarting the container)
docker exec documentserver
cat /etc/onlyoffice/documentserver/local.json |
jq -r '.services.CoAuthoring.secret | to_entries[] | .value.string'If jq isn’t installed, a grep -A 1 'string' loop over the file works
the same way.
The two traps
1. Secret mismatch after upgrade
A lot of “DocumentServer keeps rejecting my JWT” GitHub issues come from
this exact scenario: the connector was configured for the old secret,
and either the user (or an automatic upgrade script) generated a new
random one. The DocumentServer now sits with two equal-but-different
secrets across versions. The fix is the same string in both places,
exactly — including no leading/trailing whitespace.
2. JWT_IN_BODY=false vs true defaults changed across versions
In v6.x, the default for JWT_IN_BODY was false. In some v7.x point
releases, depending on how you migrated, it can end up true. If your
connector works at one moment and breaks the next, check this flag.
docker exec documentserver cat /etc/onlyoffice/documentserver/local.json
| jq '.services.CoAuthoring.token.enable | .browser, (.request.inbox, .request.outbox)'You should see true for all three. If any are false, you overrode them
during the migration.
What I actually run in 2026
For a Docker deploy on a single VM, with the connector config held in a
file under version control:
# docker-compose.yaml
services:
documentserver:
image: onlyoffice/documentserver
restart: unless-stopped
environment:
JWT_ENABLED: "true"
JWT_SECRET_FILE: /run/secrets/jwt_secret
JWT_HEADER: Authorization
volumes:
- /var/run/secrets/jwt_secret:/run/secrets/jwt_secret:ro
- documentserver_data:/var/lib/onlyoffice
ports:
- "8080:80"Generating the secret via Docker secrets (openssl rand -hex 32 | docker secret create jwt_secret -) means I can rotate it by re-seeding
the secret without touching docker-compose.yaml itself. The
JWT_SECRET_FILE env var reads the secret from a file path —
available since v7.4 — and is the right pattern when you’re running
under Swarm or any orchestrator with a secrets store.
Sources
- ONLYOFFICE Docs official JWT config docs: https://helpcenter.onlyoffice.com/docs/installation/docs-configure-jwt.aspx
- ONLYOFFICE Docs troubleshooting (JWT section): https://helpcenter.onlyoffice.com/docs/installation/docs-troubleshooting-linux.aspx
- ONLYOFFICE DocumentServer on Docker Hub: https://hub.docker.com/r/onlyoffice/documentserver
Comments