This section covers running Apache Tika as a REST server via tika-server.
Tika Server provides a RESTful HTTP interface for parsing documents and extracting content. It can be deployed as a standalone service or in a containerized environment.
In Tika 4.x, the main content-extraction endpoints — /tika, /rmeta,
/unpack, and /meta — parse in forked child processes via the Tika Pipes
infrastructure. This provides process isolation (a parser crash or OOM in a
child cannot take down the request-handling process) at the cost of requiring
a Pipes configuration. See
Migrating Tika Server to 4.x
for the full breaking-change list when upgrading from 3.x.
|
Important
|
This is not opt-in the way |
|
Important
|
The primary rule is trusted callers only. tika-server is not a security boundary:
it performs no authentication or authorization, and parsing untrusted documents is inherently
risky. Only expose it to trusted callers on a trusted network — never directly to untrusted users
or the public internet — and put your own authentication, authorization, and network controls in
front of it.
|
allowPipes and allowPerRequestConfig (both off by default) are defense in depth, not security
boundaries: they reduce what a caller can reach, but they do not make it safe to expose the
server to untrusted callers. tika-grpc is even more exposed by default. See
Security for the shared trust model and
Tika gRPC for the gRPC specifics.
java -jar tika-server-standard-X.Y.Z.jarThe server starts on localhost:9998 by default.
| Option | Description |
|---|---|
|
Hostname to bind to. Default |
|
Listen port. Default |
|
Path to |
|
Path to the Tika Pipes plugins configuration file. |
|
Server ID, surfaced in the |
|
Print the usage message. |
|
Note
|
Other behavior — allowPipes, allowPerRequestConfig, CORS, TLS, timeouts — is
configured in the JSON config file (see Configuration), not via CLI flags.
|
For the canonical endpoint inventory, including the PUT vs POST split and the
multipart-config pattern introduced in 4.x, see the
New /tika Endpoint Structure
section of the migration guide. The most-used endpoints are summarized below.
Simple PUT — the entire request body is the document, no metadata:
# Default: raw XHTML
curl -T document.pdf http://localhost:9998/tika
# Explicit handler
curl -T document.pdf http://localhost:9998/tika/text
curl -T document.docx http://localhost:9998/tika/html
curl -T document.docx http://localhost:9998/tika/md
curl -T document.pdf http://localhost:9998/tika/jsonPOST with multipart for custom per-request configuration:
curl -X POST http://localhost:9998/tika/json \
-F "file=@document.pdf" \
-F "config={\"pdf-parser\":{\"ocr\":{\"strategy\":\"no_ocr\"}}};type=application/json"Valid handler paths under /tika/: text, html, xml, md, json. For
the JSON variant, you can also nest a handler — /tika/json/text,
/tika/json/html, etc. — to choose the content-field format inside the JSON
envelope; that nested handler accepts the full set (text, html, xml,
md, markdown, body, ignore).
Returns metadata for the container document and all embedded documents as a JSON array of metadata objects. The handler controls the content field of each entry:
curl -T document.pdf http://localhost:9998/rmeta # default: markdown
curl -T document.pdf http://localhost:9998/rmeta/text
curl -T document.pdf http://localhost:9998/rmeta/html
curl -T document.pdf http://localhost:9998/rmeta/xml
curl -T document.docx http://localhost:9998/rmeta/markdown # or /md
curl -T document.pdf http://localhost:9998/rmeta/ignore # metadata onlyReturns container-document metadata only (no recursive embedded list, no content):
curl -T document.pdf http://localhost:9998/meta
curl -T document.pdf http://localhost:9998/meta/Content-Type # single field-
/version— server version -
/status— health/status (includes server ID) -
/parsersand/parsers/details— registered parsers -
/detectors— registered detectors -
/mime-types— known MIME types -
/detect/stream— type detection only (no parsing) -
/language/stream,/language/string— language detection -
/translate/all/{translator}/{src}/{dest}— translation -
/pipes,/async— Pipes-based bulk processing
|
Note
|
/pipes and /async require allowPipes (they drive process-isolated fetching
and parsing); selecting either without it causes the server to refuse to start. /status
is a plain opt-in endpoint — enable it simply by listing it under endpoints. See
Security Configuration.
|
tika-server distinguishes two different kinds of failure: the forked worker itself dying, and the worker running fine but catching an exception while parsing one particular document. They get different treatment.
When parsing fails due to a process-level problem — the forked child process timed out,
ran out of memory, or crashed unexpectedly — the server returns an HTTP error with a
JSON body whose shape matches the PipesResult status:
{"status": "TIMEOUT"}The status field is the PipesResult.RESULT_STATUS enum name. By default the body
carries only the status. When the server is configured with returnStackTrace=true,
a message field is also included (it often contains a server-side stack trace), e.g.
{"status": "TIMEOUT", "message": "Task timed out after 60000ms"}.
| HTTP status | status values |
Meaning |
|---|---|---|
|
|
The forked parse process actually failed (crashed, OOM’d, or exceeded its timeout). The server is still healthy; the client may retry. |
|
|
Nothing failed — no parse client became available within the configured wait time
(deliberate backpressure, not a bug; see Endpoints
and Forked-Process Groups). Distinct from |
|
|
Server misconfiguration or a task-level infrastructure error. Retrying the same document on the same server is unlikely to succeed without a configuration fix. |
A process-level failure (above) means the worker itself is gone — nothing was parsed. A per-document parse exception is different: the worker ran to completion and simply caught an exception while parsing this one document (an encrypted file with no password, a malformed embedded object, an NPE in a specific parser). The worker is healthy, and whatever content it managed to extract is still available.
Which HTTP status this gets depends on whether the response shape has room to embed the exception alongside content:
| Endpoints | Status | Behavior |
|---|---|---|
|
|
The exception is embedded in the response’s |
|
|
A raw byte-stream response has no field to embed the exception in, so the status itself signals the failure — but the body still carries whatever content was actually extracted, not an empty or generic error body. |
|
|
A single scalar value has nowhere to embed the exception either, so it’s thrown rather than silently returned as if the field were simply absent. |
|
|
Same reasoning as the raw endpoints, but content is not currently preserved — any files already unpacked before the exception are discarded. This is a known gap, not yet addressed. |
By default (returnStackTrace=false), any exception text exposed this way is trimmed
to just the exception’s class and message — not the full stack trace, which can
reveal internal file paths and library internals. For the 200 OK family the
trimmed field is still always present when a failure occurred, so callers can detect
it either way; for the 422 family, the body carries no exception text at all unless
returnStackTrace=true. Set returnStackTrace=true to get the full trace — useful
in development, best left off in production.
Server behavior beyond host/port is controlled by a JSON config file passed via
-c/--config. The server section in that file maps to fields on
TikaServerConfig; commonly-set fields include:
| Field | Default | Description |
|---|---|---|
|
|
Opt-in for the |
|
all defaults |
Which endpoints to expose. Leave unset to get the full default set (includes |
|
|
Opt-in for per-request parser configuration: the |
|
|
|
|
|
Include parser stack traces in error responses. Useful in dev, dangerous in production (leaks internals). |
|
|
Compute a digest of the parsed bytes. Comma-separated algorithm names: |
|
|
Max bytes buffered for digest computation. |
|
inherited |
|
|
random UUID |
Override the auto-generated server ID (the |
For the full Pipes-related sections (pipes, fetchers, emitters, parse-context)
that tika-server 4.x requires, see
Configuration Changes.
Two independent forked-process groups exist:
-
/tika+/rmeta+/unpack+/meta+/pipesshare one group — all five go through the samePipesParsingHelper/PipesParser, sized bypipes.numClients./pipesstill requiresallowPipesto actually start (the server refuses to start if it’s listed without that flag) even though it shares its parser with the always-on endpoints; the others don’t requireallowPipes. -
/asyncis a separate group (gated behindallowPipes) — it doesn’t share aPipesParserwith the group above at all. It manages its own forked-worker pool directly (queued/background processing, results delivered via a configuredPipesReporterrather than in the HTTP response), sized by its own read ofpipes.numClientsfrom the same config.
Within a pipes-backed group, numClients does two separate jobs, and it’s
worth understanding both before picking a value.
Each group holds a fixed pool of numClients workers. A request that arrives
when all of them are busy doesn’t fail immediately — it waits, up to
pipes.maxWaitForClientMillis (default 60s), for one to free up. This is
deliberate backpressure, not a bug: if a worker frees up in time, the request
is served normally; if the wait times out, the server returns 429 with
status: CLIENT_UNAVAILABLE_WITHIN_MS — an explicit "I’m at capacity, retry"
signal, not a crash (see Error Responses above). Under
3.x’s in-process model there was no equivalent hard cap — requests just piled
up on the HTTP server’s own thread pool instead. If you’re seeing
CLIENT_UNAVAILABLE_WITHIN_MS under real load, that’s this group’s
concurrency limit telling you it’s undersized for your request volume: raise
numClients for more concurrent capacity, or tune maxWaitForClientMillis to
fail faster (surface backpressure to the caller sooner) or more patiently
(absorb bursts, at the cost of tying up more request threads while waiting).
Independently of the above, each group also auto-sizes its forked JVMs'
-XX:ActiveProcessorCount from numClients and the host’s core count — see
Forked-JVM CPU Sizing for the full mechanics. This
part can go wrong across groups: the auto-sizer for one group has no
visibility into the other group running in the same process, so if you enable
/async alongside the shared group — a config listing async together with
any of tika/rmeta/unpack/meta/pipes, or simply leaving endpoints
unset while allowPipes=true gives you both groups at once — each group’s
auto-sizer computes its slice as if it owned the whole host. Whether that
actually causes oversubscription depends on your numClients values relative
to the host’s core count; it’s not automatic, but it’s also not something the
auto-sizer will warn you about, because from either group’s perspective alone
the sizing looks fine. See
Known
limitation: multiple Pipes groups in one process for the mechanics and
mitigation (scope endpoints to what you actually use, or set
-XX:ActiveProcessorCount explicitly with the combined total in mind).
-
TLS/SSL Configuration — Secure your server with TLS and mutual authentication
-
Migrating Tika Server to 4.x — Breaking changes from 3.x
By default, the /config family of endpoints that expose server configuration are
disabled. These endpoints can reveal sensitive information about your server,
including parser settings and system properties (see
CVE-2015-3271).
Protected endpoints include:
-
/tika/configand/tika/config/{text,html,xml,md,json}— POST with multipart config -
/rmeta/config— POST with multipart config -
/meta/config— POST with multipart config
The setting is JSON-only — there is no CLI flag. Set allowPerRequestConfig in
your config file’s server section:
{
"server": {
"allowPerRequestConfig": true
}
}|
Warning
|
Only enable allowPerRequestConfig if you have secured access to Tika
Server through network controls (firewalls, private subnets), a reverse proxy
(nginx, Apache httpd), or
2-way TLS authentication. Exposing config endpoints
to untrusted networks can help attackers identify vulnerabilities and craft
targeted attacks.
|
The /pipes and /async endpoints require allowPipes. They drive process-isolated
batch parsing through your configured fetchers and emitters — whoever can reach them
gains the read access of your fetchers and the write access of your emitters (see
CVE-2015-3271).
In earlier releases these endpoints were enabled simply by listing them under
server.endpoints. You must now also set allowPipes to true; selecting either
without it causes the server to refuse to start. This is deliberate — it makes enabling
these powerful endpoints an explicit, considered choice.
{
"server": {
"allowPipes": true,
"endpoints": ["tika", "rmeta", "pipes", "async", "status"]
}
}|
Note
|
/status exposes only aggregate counters (active task count, files processed,
time since last parse) and is not gated by allowPipes or allowPerRequestConfig.
Enable it by listing status under endpoints.
|
-
Keep config endpoints disabled in production (default behavior).
-
Use network controls to restrict access (firewall rules, private subnets).
-
Consider TLS for encrypted communication — see TLS Configuration.
-
Run with minimal privileges — don’t run Tika Server as root.
-
Monitor logs for unusual access patterns.