TLS/SSL Configuration
Tika Server supports TLS for encrypted communication, in two flavours:
-
1-way TLS — the server presents its certificate; clients verify the server.
-
2-way TLS (mTLS) — both sides present certificates and authenticate each other.
Should you configure TLS here at all?
For most deployments, put Tika Server behind a reverse proxy (nginx, Apache httpd, Traefik) that terminates TLS, and run Tika itself on plain HTTP on localhost or a private network. That simplifies certificate management and gives you rate limiting and authentication for free. Tika Server has no built-in token support, so JWT / API-key / OAuth2 authentication has to come from a proxy or API gateway anyway.
Configure TLS directly on Tika Server (the rest of this page) when you cannot use a proxy, need client-certificate authentication, or prefer a single-process deployment. If you only need to restrict access and not encrypt it, network controls — binding to localhost, firewall rules, a private subnet — are simpler than either.
Quick Start
TLS settings live in tlsConfig inside the server section of tika-config.json. Pass the
file with -c/--config.
1-Way TLS
Server authenticates to clients; clients present no certificate.
{
"server": {
"host": "localhost",
"port": 9998,
"tlsConfig": {
"active": true,
"keyStoreType": "PKCS12",
"keyStoreFile": "/path/to/server-keystore.p12",
"keyStorePassword": "your-password",
"clientAuthenticationWanted": false,
"clientAuthenticationRequired": false
}
}
}
2-Way TLS (mTLS)
Add a truststore holding the client certificates you trust, and require client authentication.
{
"server": {
"host": "localhost",
"port": 9998,
"tlsConfig": {
"active": true,
"keyStoreType": "PKCS12",
"keyStoreFile": "/path/to/server-keystore.p12",
"keyStorePassword": "your-password",
"trustStoreType": "PKCS12",
"trustStoreFile": "/path/to/server-truststore.p12",
"trustStorePassword": "your-password",
"clientAuthenticationWanted": true,
"clientAuthenticationRequired": true
}
}
}
Configuration Reference
All properties below go in server.tlsConfig.
| Property | Type | Default | Description |
|---|---|---|---|
|
boolean |
|
Enable TLS. When true, the server serves HTTPS. |
|
string |
null |
Keystore format: |
|
string |
null |
Path to the server’s keystore, holding its private key and certificate. |
|
string |
null |
Password for the keystore. |
|
string |
null |
Truststore format: |
|
string |
null |
Path to the truststore holding trusted client certificates (2-way TLS). |
|
string |
null |
Password for the truststore. |
|
boolean |
|
Request client certificates but do not require them; clients without one still connect. |
|
boolean |
|
Require a client certificate trusted by the server. |
|
list |
|
TLS versions to enable. TLS 1.0 and 1.1 are insecure and off by default. |
|
list |
null |
TLS versions to explicitly disable. |
|
list |
null |
Cipher suites to enable. Null means JVM defaults. |
|
list |
null |
Cipher suites to disable. Entries are regular expressions. |
|
integer |
|
Days before certificate expiration to log a warning. |
When active is true, keyStoreType, keyStoreFile and keyStorePassword are all required.
Truststore properties are all-or-nothing: set all three or none.
Restricting protocols and cipher suites
Add to server.tlsConfig — for example, TLS 1.3 only:
"includedProtocols": ["TLSv1.3"]
An explicit allowlist of cipher suites:
"includedCipherSuites": [
"TLS_AES_256_GCM_SHA384",
"TLS_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
]
Or a denylist of weak ones, as regular expressions:
"excludedCipherSuites": [".*CBC.*", ".*RC4.*", ".*3DES.*", ".*NULL.*"]
Certificate Expiration Warnings
At startup, Tika Server checks every certificate in the configured stores and logs:
-
ERROR — already expired, or not yet valid; TLS will fail.
-
WARN — expires within
certExpirationWarningDays(default 30). -
DEBUG — valid.
WARN TlsConfig - Certificate 'server' in keystore expires in 15 days on Sat Feb 15 12:00:00 UTC 2026. Consider renewing soon.
ERROR TlsConfig - Certificate 'server' in keystore has EXPIRED on Mon Jan 01 12:00:00 UTC 2026. TLS connections will fail!
Set "certExpirationWarningDays": 60 to warn earlier, or 0 to disable the check entirely.
Generating Test Certificates
Self-signed certificates for testing only. In production use certificates from a trusted CA.
# Server key pair, and its certificate for clients to trust
keytool -genkeypair -alias server -keyalg RSA -keysize 2048 -validity 365 \
-keystore server-keystore.p12 -storetype PKCS12 -storepass changeit \
-dname "CN=localhost,OU=Tika,O=Apache,L=Unknown,ST=Unknown,C=US"
keytool -exportcert -alias server -keystore server-keystore.p12 \
-storetype PKCS12 -storepass changeit -file server.crt
# Client key pair (2-way TLS only), and its certificate for the server to trust
keytool -genkeypair -alias client -keyalg RSA -keysize 2048 -validity 365 \
-keystore client-keystore.p12 -storetype PKCS12 -storepass changeit \
-dname "CN=TikaClient,OU=Tika,O=Apache,L=Unknown,ST=Unknown,C=US"
keytool -exportcert -alias client -keystore client-keystore.p12 \
-storetype PKCS12 -storepass changeit -file client.crt
# Truststores: the server trusts the client cert, the client trusts the server cert
keytool -importcert -alias client -file client.crt -noprompt \
-keystore server-truststore.p12 -storetype PKCS12 -storepass changeit
keytool -importcert -alias server -file server.crt -noprompt \
-keystore client-truststore.p12 -storetype PKCS12 -storepass changeit
Testing
# 1-way TLS: trust the server certificate
curl --cacert server.crt https://localhost:9998/tika
# 2-way TLS: also present a client certificate
curl --cacert server.crt --cert client.crt --key client-key.pem \
https://localhost:9998/tika
# Self-signed, throwaway testing only -- skips verification entirely
curl -k https://localhost:9998/tika
From Java:
// 2-way TLS
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(
new File("client-keystore.p12"),
"changeit".toCharArray(),
"changeit".toCharArray())
.loadTrustMaterial(
new File("client-truststore.p12"),
"changeit".toCharArray())
.build();
HttpClient client = HttpClients.custom()
.setSSLContext(sslContext)
.build();
HttpResponse response = client.execute(new HttpGet("https://localhost:9998/tika"));
Containers
Mount the config file and the keystores into the container, and point -c at the config. The
paths in tlsConfig are container paths, not host paths.
docker run -d --name tika-server -p 9998:9998 \
-v $(pwd)/config:/tika/config:ro \
-v $(pwd)/certs:/tika/certs:ro \
apache/tika:<version> \
-c /tika/config/tika-config.json
With /tika/config/tika-config.json holding, for example:
{
"server": {
"port": 9998,
"tlsConfig": {
"active": true,
"keyStoreType": "PKCS12",
"keyStoreFile": "/tika/certs/server-keystore.p12",
"keyStorePassword": "your-password",
"trustStoreType": "PKCS12",
"trustStoreFile": "/tika/certs/server-truststore.p12",
"trustStorePassword": "your-password",
"clientAuthenticationRequired": true
}
}
}
The official image’s entrypoint already passes -h 0.0.0.0, so the container listens on all
interfaces regardless of server.host.
|
Docker Compose
services:
tika:
image: apache/tika:latest
ports:
- "9998:9998"
volumes:
- ./config:/tika/config:ro
- ./certs:/tika/certs:ro
command: ["-c", "/tika/config/tika-config.json"]
healthcheck:
test: ["CMD", "curl", "-f", "--cacert", "/tika/certs/server.crt", "https://localhost:9998/tika"]
interval: 30s
timeout: 10s
retries: 3
Kubernetes
kubectl create secret generic tika-tls-certs \
--from-file=server-keystore.p12=./certs/server-keystore.p12 \
--from-file=server-truststore.p12=./certs/server-truststore.p12
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: tika-server
spec:
replicas: 1
selector:
matchLabels:
app: tika
template:
metadata:
labels:
app: tika
spec:
containers:
- name: tika
image: apache/tika:latest
ports:
- containerPort: 9998
args: ["-c", "/tika/config/tika-config.json"]
volumeMounts:
- name: config
mountPath: /tika/config
readOnly: true
- name: certs
mountPath: /tika/certs
readOnly: true
volumes:
- name: config
secret:
secretName: tika-config
- name: certs
secret:
secretName: tika-tls-certs
Keystore passwords
Tika reads keyStorePassword and trustStorePassword literally out of the JSON — it
does not expand environment variables, ${…} placeholders, or /run/secrets paths. Injecting
the password as an env var does nothing on its own.
|
Treat the whole config file as the secret: store it as a Kubernetes Secret or a Docker secret
and mount it read-only (as in the deployment above), rather than baking it into an image or
committing it. If you need to assemble the password at run time, do it in your own entrypoint
script, which writes the config file before starting the server.
Troubleshooting
| Message | Cause |
|---|---|
|
|
|
Wrong path. Absolute paths are safest; relative ones resolve against the working directory, which inside a container is not where you think it is. |
|
Some but not all of |
|
|
SSL handshake failure |
Expired or invalid certificate; the client does not trust the server’s certificate (1-way); the server does not trust the client’s (2-way); or no protocol/cipher suite in common. |
For handshake detail, add -Djavax.net.debug=ssl:handshake to the JVM:
java -Djavax.net.debug=ssl:handshake -jar tika-server-standard-X.Y.Z.jar -c config.json
Security Best Practices
-
Use strong keystore and truststore passwords, and never commit them.
-
Stay on TLS 1.2/1.3 (the default) and restrict cipher suites to modern algorithms.
-
Prefer PKCS12 over JKS.
-
Use CA-issued certificates in production and rotate them before expiry.
-
Use 2-way TLS when clients need to be authenticated.
-
Protect keystore files with restrictive filesystem permissions.
See Also
-
Security — general security considerations
-
Robustness — process isolation and fault tolerance