A Calibre Content Server plugin that enables authentication via reverse proxy
headers (e.g. Remote-User set by Authelia, Authentik, oauth2-proxy, etc.).
When calibre-server is deployed behind a reverse proxy with SSO (e.g. Authelia),
users must log in twice: once via the SSO portal, and again via calibre's built-in
HTTP Basic/Digest authentication. This plugin intercepts the Remote-User header
set by the reverse proxy and automatically authenticates the user in calibre.
- Download or build
reverse_proxy_auth.zip(see Building below). - Open calibre → Preferences → Plugins → Load plugin from file.
- Select the
reverse_proxy_auth.zipfile. - Restart calibre (or the content server).
- Configure: Preferences → Plugins → User interface plugins → Reverse Proxy Auth → Customize.
Comma-separated list of IP addresses or CIDR networks that are allowed to send
the Remote-User header. Only requests from these IPs will be checked for
proxy authentication. This is the primary security boundary — end users must
not be able to connect directly to calibre from an IP in this list.
Default: 127.0.0.1/32,::1/128
The HTTP header containing the authenticated username. Common values:
Remote-User— Authelia defaultX-Forwarded-User— nginx/traefik commonX-Auth-Request-User— oauth2-proxy
- Strict (default): Only allow alphanumeric, dot, dash, underscore,
@. Reject requests with invalid characters. - Sanitize: Replace invalid characters with underscores.
- Strip domain: Remove
@domainpart first, then sanitize (for proxies that sendjohn@example.com).
When enabled, users that don't exist in the calibre user database are automatically created with a 32-byte random password (cannot log in via Basic/Digest). When disabled (default), unknown users receive HTTP 403.
When enabled (default), requests without the Remote-User header fall back
to normal HTTP authentication. Disable to require proxy authentication only.
When enabled (default), the plugin activates authentication even if calibre
was started without --enable-auth. This allows using Authelia as the sole
authentication method.
location /authelia {
internal;
proxy_pass http://127.0.0.1:9091/api/verify;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
}
location / {
auth_request /authelia;
auth_request_set $auth_user $upstream_http_remote_user;
auth_request_set $auth_groups $upstream_http_remote_groups;
auth_request_set $auth_email $upstream_http_remote_email;
auth_request_set $auth_name $upstream_http_remote_name;
proxy_set_header Remote-User $auth_user;
proxy_set_header Remote-Groups $auth_groups;
proxy_set_header Remote-Email $auth_email;
proxy_set_header Remote-Name $auth_name;
# Strip any client-supplied Remote-* headers
proxy_set_header Remote-User "";
proxy_set_header Remote-Groups "";
proxy_set_header Remote-Email "";
proxy_set_header Remote-Name "";
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}Important: Set
proxy_set_header Remote-User ""before theauth_request_setlines to ensure client-supplied headers are stripped. Nginx applies proxy_set_header directives in order; setting it to empty first prevents clients from forging the header.
# Traefik ForwardAuth middleware
middlewares:
authelia:
forwardAuth:
address: http://authelia:9091/api/verify?rd=https://auth.example.com/
trustForwardHeader: true
authResponseHeaders:
- Remote-User
- Remote-Groups
- Remote-Email
- Remote-NameThe plugin provides a debug endpoint at /reverse-proxy-auth/whoami (no
authentication required):
# From a trusted IP
curl -H "Remote-User: john" http://localhost:8080/reverse-proxy-auth/whoamiResponse:
{
"remote_addr": "127.0.0.1",
"is_trusted_proxy": true,
"authenticated_user": "john",
"headers": {
"Remote-User": "john",
"Remote-Groups": "users,admins",
"Remote-Email": "john@example.com",
"Remote-Name": "John Doe"
}
}From a non-trusted IP, only remote_addr and is_trusted_proxy: false are
returned — the Remote-User header is never read.
-
IP Trust Check: Only requests from configured trusted proxy IPs/CIDRs are checked for the Remote-User header. The IP is verified against the TCP peer address (
remote_addr), which cannot be forged via HTTP headers. -
Header Stripping: The reverse proxy must strip any client-supplied
Remote-*headers before proxying to calibre. This prevents users from impersonating others. -
User Validation: Usernames are validated/sanitized according to the configured mode. Users must exist in the calibre user database (unless auto-create is enabled).
-
Safe Fallback: Missing/empty headers, invalid usernames, and errors all fall back safely — either to Basic/Digest auth or to 403, never to authentication bypass.
cd reverse_proxy_auth
zip -r ../reverse_proxy_auth.zip . -x "*.pyc" -x "__pycache__/*"- Calibre >= 9.11.0
- Python 3 (bundled with calibre)
GPL v3