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

    Server cables representing remote calls with Python xmlrpc.client
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python xmlrpc.client: Remote Calls

    Learn Python xmlrpc.client to call XML-RPC services, handle Fault and ProtocolError, use TLS, compatible types, and safe limits.

    Ler mais

    Tempo de leitura: 5 minutos
    21/08/2026
    Error code over binary data representing failures handled with Python urllib.error
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python urllib.error: Handle HTTP Failures

    Learn Python urllib.error to handle URLError, HTTPError, incomplete downloads, selective retries, and clearer network diagnostics.

    Ler mais

    Tempo de leitura: 5 minutos
    21/08/2026
    HTML keycaps representing HTML entities with Python html.entities
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    html.entities: Convert HTML Entities

    Learn Python html.entities to inspect HTML entities, convert names and code points, and avoid confusing decoding with sanitization.

    Ler mais

    Tempo de leitura: 6 minutos
    21/08/2026
    Folder with files representing MIME types identified with Python mimetypes
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    mimetypes: Detect MIME Types for Files

    Learn Python mimetypes to identify file types, validate uploads, and set safer HTTP Content-Type headers.

    Ler mais

    Tempo de leitura: 6 minutos
    20/08/2026
    HTML code on a screen representing parsing with Python html.parser
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python html.parser: Parse HTML

    Learn Python html.parser to extract text, links, and metadata, process HTML incrementally, and avoid confusing parsing with sanitization.

    Ler mais

    Tempo de leitura: 5 minutos
    20/08/2026
    Server rack representing low-level HTTP connections with Python http.client
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python http.client: Low-Level HTTP

    Learn Python http.client for low-level HTTP and HTTPS connections, streaming, headers, TLS, connection reuse, size limits, and errors.

    Ler mais

    Tempo de leitura: 4 minutos
    20/08/2026