xmlrpc.server: Build XML-RPC Servers

Published on: August 21, 2026
Reading time: 5 minutes
Server rack representing an endpoint built with Python xmlrpc.server

The xmlrpc.server module provides a basic framework for XML-RPC servers written in Python. It receives HTTP POST requests containing XML, converts parameters into Python objects, executes a registered function, and serializes the result. It can support legacy integrations, local tests, and controlled internal systems that already depend on the protocol.

The official documentation warns that the module is not secure against maliciously constructed XML data. In addition, SimpleXMLRPCServer does not provide a complete authentication system, rate limiting, abuse protection, or an internet-ready deployment architecture. Treat it as an internal component behind additional controls.

A minimal server

from xmlrpc.server import SimpleXMLRPCServer

with SimpleXMLRPCServer(
    ("127.0.0.1", 8000),
    allow_none=False,
    logRequests=False,
    use_builtin_types=True,
) as server:

    @server.register_function(name="add")
    def add(a: int, b: int) -> int:
        return a + b

    server.serve_forever()

Binding to 127.0.0.1 prevents direct connections from other machines. For an internal production service, place an authenticated reverse proxy in front and keep the process on a restricted network.

Restricting the RPC path

The default request handler accepts / and /RPC2. A subclass can allow only one path.

from xmlrpc.server import (
    SimpleXMLRPCRequestHandler,
    SimpleXMLRPCServer,
)

class Handler(SimpleXMLRPCRequestHandler):
    rpc_paths = ("/RPC2",)

server = SimpleXMLRPCServer(
    ("127.0.0.1", 8000),
    requestHandler=Handler,
)

Path restriction removes accidental endpoints, but it is not authentication. A client that can reach the path can still submit method calls.

Explicit function registration

Prefer register_function() with clear public names. It creates an allowlist of exposed operations.

def status() -> dict[str, object]:
    return {"ok": True, "version": "1"}

server.register_function(status, "system.status")

Validate types, ranges, string lengths, and collection counts inside each function. Python type annotations do not validate incoming RPC parameters automatically.

Do not expose dangerous functions

Avoid registering functions that execute commands, open arbitrary paths, evaluate code, import modules, or accept raw SQL. XML-RPC is only the transport; all risks of the underlying operation remain.

from pathlib import Path

ROOT = Path("/srv/reports").resolve()

def read_report(name: str) -> str:
    if not name.endswith(".txt") or "/" in name or "\\" in name:
        raise ValueError("invalid name")
    path = (ROOT / name).resolve()
    if ROOT not in path.parents:
        raise ValueError("path outside root")
    return path.read_text(encoding="utf-8")

Even on an internal network, treat every parameter as untrusted.

register_instance and _dispatch

register_instance() can expose methods from an object. A safer design implements _dispatch() and maps allowed names explicitly.

class Service:
    def _dispatch(self, method, params):
        allowed = {
            "system.status": self.status,
            "calculate.add": self.add,
        }
        function = allowed.get(method)
        if function is None:
            raise ValueError("method not allowed")
        return function(*params)

    def status(self):
        return {"ok": True}

    def add(self, a, b):
        return int(a) + int(b)

server.register_instance(Service())

The allowlist prevents internal attributes from becoming remotely discoverable.

Never enable allow_dotted_names on an open network

The Python documentation explicitly warns that allow_dotted_names=True can expose module globals and may allow arbitrary code execution. Keep the default False. If a legacy client needs hierarchical method names, register those names explicitly or implement a controlled _dispatch().

Introspection

register_introspection_functions() exposes system.listMethods, system.methodHelp, and system.methodSignature.

server.register_introspection_functions()

This helps during development, but it reveals method surface. Enable it in production only when necessary and protected. Public documentation should never list administrative operations unintentionally.

Multicall

register_multicall_functions() enables system.multicall, allowing several operations in one request.

server.register_multicall_functions()

Batching reduces network round trips but increases work per request. Limit the number of calls, total cost, and response size. Do not batch destructive operations without defined atomicity and partial-failure behavior.

Types and allow_none

XML-RPC supports a limited set of types. allow_none=True enables an extension for None that some clients do not implement. use_builtin_types=True simplifies date and binary handling.

Document each method’s accepted types, limits, date timezone, maximum binary size, and meaning of missing values. For large files, use a dedicated upload service.

Faults and error messages

Exceptions raised by registered functions become XML-RPC faults. Do not return stack traces, filesystem paths, SQL, or credentials. Convert known domain failures into stable codes.

from xmlrpc.client import Fault

def get_user(user_id: int):
    if user_id <= 0:
        raise Fault(400, "invalid identifier")
    user = repository.get(user_id)
    if user is None:
        raise Fault(404, "user not found")
    return user

The message still needs escaping when a client displays it in HTML.

Authentication

SimpleXMLRPCServer has no complete modern authentication facility. A safer architecture places it behind Nginx, Apache, an API gateway, or a service mesh that validates mTLS, Basic Authentication, tokens, or workload identity.

A custom request handler can inspect headers, but ensure credentials never reach logs. Centralize the policy and test 401 and 403 responses.

TLS

The simple server does not offer an ergonomic production HTTPS setup. Terminate TLS at an updated reverse proxy that manages certificates, protocol versions, body limits, timeouts, and access logs.

Wrapping the socket manually makes your application responsible for handshake, shutdown, and security updates. The guide to Python ssl explains the risks.

Body limits and malicious XML

The documentation’s warning must be taken seriously. XML can be crafted to consume memory or CPU. Enforce a Content-Length limit at the proxy, reject unsupported transfer behavior, configure timeouts, and cap concurrent connections.

Do not expose the server to anonymous clients. Network segmentation and authentication reduce risk but do not replace resource limits.

Concurrency

SimpleXMLRPCServer is based on socketserver.TCPServer and handles requests synchronously. One slow method blocks later requests.

from socketserver import ThreadingMixIn
from xmlrpc.server import SimpleXMLRPCServer

class ThreadedXMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer):
    daemon_threads = True

Threads increase concurrency but also introduce races, resource consumption, and overload. Protect shared state, limit connections, and avoid long-running operations. The guide to Python socketserver covers mixins and shutdown.

Timeouts and long jobs

Do not run large reports, video processing, or unbounded tasks in the request thread. Queue the work, return a job identifier, and expose a status method. Configure deadlines at both proxy and client.

Graceful shutdown

Use the server as a context manager and call shutdown() from a different thread while serve_forever() is active. Then close application resources.

try:
    server.serve_forever()
except KeyboardInterrupt:
    pass
finally:
    server.server_close()

In managed environments, handle SIGTERM and stop accepting new work before process termination.

Logging

logRequests=True provides simple request logs. Production systems should use structured events containing logical method name, duration, summarized result, and correlation identifier. Do not log complete XML bodies, tokens, or personal data.

Generated documentation

DocXMLRPCServer can serve an HTML documentation page for GET requests. It is convenient in a lab, but it may reveal method names and internal documentation. Do not expose it without authentication and review.

The matching client

Consume the service with Python xmlrpc.client. The client should use HTTPS, a timeout transport, a method allowlist, and separate handling for Fault and ProtocolError.

Test valid and missing methods, out-of-range parameters, oversized bodies, malformed XML, missing authentication, wrong paths, concurrent calls, excessive multicall, graceful shutdown, and the absence of secrets in faults and logs.

Run local tests and never use a public third-party server to validate destructive code.

When to choose another technology

For a new API, HTTP frameworks with JSON, schemas, and security middleware are generally more appropriate. XML-RPC is mainly useful for compatibility with existing systems or simple internal tools on a controlled network.

Conclusion

xmlrpc.server can create an RPC endpoint quickly, but speed does not remove security responsibilities. Register functions explicitly, keep allow_dotted_names disabled, restrict paths and payloads, authenticate at a proxy, protect TLS, control concurrency, and treat all XML as untrusted.

Read the official xmlrpc.server documentation and Python’s XML security guidance. For public internet services or new projects, consider an API platform with more complete controls.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Developer configuring a Python HTTPS server and TLS certificate
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: Build a Local HTTPS Server in Python

    Learn Python HTTPSServer for local HTTPS services, TLS certificates, threaded handling, testing, and practical security limits.

    Ler mais

    Tempo de leitura: 4 minutos
    08/10/2026
    Protected files representing secure TAR extraction with Python
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: Extract TAR Safely

    Learn Python tarfile extraction_filter for safer TAR extraction, path validation, links, permissions, and resource limits.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026
    Python code showing warnings controlled with catch_warnings
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: Capture Python Warnings in Tests

    Learn Python catch_warnings to capture, test, and control warnings with focused filters and safe temporary scopes.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Python code representing persistent pickle references
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: Serialize External References

    Learn Python pickle persistent_id for stable external references, validation, security, performance, and long-term compatibility.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Binary code representing Python buffers and memory views
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: Count Values Without Buffer Copies

    Learn Python memoryview.count to count bytes and values in buffers without copies, with formats, limits, and practical safety.

    Ler mais

    Tempo de leitura: 5 minutos
    06/10/2026
    Python code and type annotations
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: Avoid Circular Imports in Annotations

    Learn Python annotationlib to inspect deferred annotations, avoid circular imports, and build safer runtime tooling.

    Ler mais

    Tempo de leitura: 5 minutos
    06/10/2026