Celery's default serializer is JSON, and that is the safe choice. Pickle can serialize almost any Python object but is inherently insecure when clients are untrusted or unauthenticated. Set accept_content to json so workers refuse other formats, protect the broker like a database, and use message signing if you need richer payloads.
What Celery's security guide says
The Celery security page starts with a frank line: while Celery is written with security in mind, it should be treated as an unsafe component. Then it covers the areas of concern.
On serializers, it explains that JSON has been the default since version 4.0. Because JSON supports a restricted set of types, you may be tempted to switch to pickle. The guide acknowledges pickle's convenience, even functions with some work, and then says that for the same reasons pickle is inherently insecure and should be avoided whenever clients are untrusted or unauthenticated.
Why the broker matters here
A Celery worker reads messages from a broker and, by default, trusts that the data has not been tampered with. If the worker accepts pickle, anyone who can place a message on the queue can make the worker run code. So the broker is part of your trust boundary.
The guide advises:
- Put a firewall in front of the broker and allow only listed machines.
- Do not trust the firewall blindly, since misconfiguration and temporary disabling are common.
- Use fine grained access control where the broker supports it.
- Enable end to end TLS and authentication with broker_use_ssl where supported.
Lock down accepted content
The simplest control is a whitelist of content types:
accept_content = ["json"]
Celery says this lets you disable untrusted content. The setting takes serializer names or content types, so application/json also works. Pair it with task_serializer = "json" and the result serializer you actually use, so no path quietly allows pickle.
If you really need more than JSON
Celery offers the auth serializer, which uses public key cryptography so that clients sign messages with a private key and workers verify them with a certificate. To enforce it, set task_serializer and event_serializer to auth, set accept_content to auth, configure the key, certificate and certificate store paths, and call setup_security(). The guide notes this also disables all insecure serializers, and that signing does not encrypt message contents, so add encryption separately if you need it.
| Option | Payload types | Trust model |
|---|---|---|
| JSON with accept_content json | Basic types | Workers never unpickle data |
| Pickle, broker locked down | Most Python objects | Anyone who can write to the queue runs code |
| auth serializer | Signed messages | Only holders of the signing key are accepted |
For most teams the better move is to pass identifiers in JSON and have the task load objects from the database.
Worker privileges
The worker guide also reminds you that tasks run with the same permissions as the worker, with the same network access as the machine. Run workers as an unprivileged user, restrict outbound traffic, and isolate them from secrets they do not need.
Review checklist
- Search configuration for serializer, accept_content, task_serializer and result_serializer.
- Confirm no pickle remains, including in result backends.
- Check who can publish to each queue and whether the broker is reachable from the internet.
- Check broker credentials are in a secret manager.
- Run workers with least privilege.
Try it yourself
Click through it at your own pace, or open it full screen. Look at how insecure deserialization settings in code and config get flagged.
Quick check
Is JSON the default in Celery?
Yes, since version 4.0 according to the Celery security guide.
Does the auth serializer encrypt messages?
No. It signs them. The guide says encryption must be enabled separately if you need it.
Safeguard's SAST and dependency scanning can flag unsafe serializer settings and outdated Celery versions. Try a free scan.
Sources
Last reviewed: 19 October 2026.