Skip to main content

fcgisgi

A FastCGI-to-ASGI/WSGI adapter.

Run ASGI/WSGI Python applications behind legacy FastCGI-based web hosting servers (e.g., Apache mod_fcgid). An alternative to flup.server.fcgi package.

PyPI - Version

Features

  • FastCGI-to-ASGI adapter.
  • FastCGI-to-WSGI adapter (via thread pool).
  • asyncio-based server implementation.
  • Support spawning from mod_fcgid.

Installation

pip install fcgisgi

Usage

ASGI

import asyncio
from fcgisgi import run_asgi_server

async def app(scope, receive, send):
    assert scope['type'] == 'http'
    await send({
        'type': 'http.response.start',
        'status': 200,
        'headers': [
            (b'content-type', b'text/plain'),
        ],
    })
    await send({
        'type': 'http.response.body',
        'body': b'Hello, world!',
    })

if __name__ == "__main__":
    # Bind to a TCP port or a Unix socket
    asyncio.run(run_asgi_server(app, bind_address=("127.0.0.1", 9000)))
    # asyncio.run(run_asgi_server(app, bind_address="/var/run/fcgisgi.sock"))

    # Alternatively, use the default FastCGI socket (fd=0) inherited from
    # the parent process (e.g., Apache mod_fcgid).
    # asyncio.run(run_asgi_server(app))

WSGI

import asyncio
from fcgisgi import run_wsgi_server

def app(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    return [b"Hello, world!"]

if __name__ == "__main__":
    asyncio.run(run_wsgi_server(app, bind_address=("127.0.0.1", 9000)))

Using mod_fcgid

When using Apache with mod_fcgid, the SCRIPT_NAME parameter often includes the FastCGI script filename (e.g., /index.fcgi), which can interfere with your application's routing.

Use the force_script_name option to override the mount point (root path) of your application, ensuring the routing engine receives the expected path.

.htaccess

AddHandler fcgid-script .fcgi
RewriteEngine On
RewriteBase /
# Static files
RewriteRule ^(static|assets|\.well-known)/ - [L]
RewriteRule ^(favicon\.ico|robots\.txt)$ - [L]
# Route everything else to the fcgi script
RewriteRule ^.* index.fcgi/$0 [QSA,END]

index.fcgi

#!/bin/sh
PATH=/path/to/venv/bin:$PATH
export PATH
exec python entrypoint.py

entrypoint.py

import asyncio
from fcgisgi import run_asgi_server

async def app(scope, receive, send):
    """ SNIP """

if __name__ == "__main__":
    # Specify the mount point.
    #     ""     if mounted at the root
    #     "/app" if mounted under "http://example.com/app/"
    asyncio.run(run_asgi_server(app, force_script_name=""))

Configuration

You can pass additional configuration parameters to run_asgi_server or run_wsgi_server via keyword arguments:

  • startup_timeout (float): Timeout for ASGI lifespan startup (default: 55.0).
  • shutdown_timeout (float): Timeout for graceful shutdown on SIGTERM (default: 55.0).
  • max_workers (int): Maximum number of worker threads for WSGI applications (passed to ThreadPoolExecutor).
  • force_script_name (str): Override the SCRIPT_NAME (WSGI) or root_path (ASGI) parameter. Useful for normalizing routing behind a prefix.

Example:

asyncio.run(run_asgi_server(
    app, 
    startup_timeout=30.0, 
    shutdown_timeout=30.0
))

FCGI_PARAMS

The original FCGI_PARAMS passed from the web server can be retrieved as a list of (bytes, bytes) key-value pairs, preserving their original order and any duplicates.

  • ASGI: scope['extensions']['fcgisgi']['fcgi_params'] in the connection/request scope.
  • WSGI: environ['fcgisgi.fcgi_params'] in the environ dictionary.

ASGI Middleware Example

You can use a middleware to expose custom FastCGI parameters (e.g., RAW_URI) as HTTP headers:

class FCGIParamToHeaderMiddleware:
    def __init__(self, app, param_name=b"RAW_URI", header_name=b"x-fcgi-raw-uri"):
        self.app = app
        self.param_name = param_name
        self.header_name = header_name

    async def __call__(self, scope, receive, send):
        if scope["type"] == "http":
            fcgi_params = scope.get("extensions", {}).get("fcgisgi", {}).get("fcgi_params", [])
            for key, value in fcgi_params:
                if key == self.param_name:
                    scope["headers"].insert(0, (self.header_name, value))
                    break
        return await self.app(scope, receive, send)

References

License

MIT

Release files for fcgisgi 0.2.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fcgisgi 0.2.3
File Size Uploaded
fcgisgi-0.2.3.tar.gz 32.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fcgisgi 0.2.3
File Interpreter ABI Platform
fcgisgi-0.2.3-py3-none-any.whl Python 3 none any Details

Total release size: 50.2 kB

Release files / fcgisgi-0.2.3.tar.gz

Download URL fcgisgi-0.2.3.tar.gz
Size 32.7 kB
Tags Source
SHA-256 checksum
How to use checksums
de65bce2c7a370d6655cf73008285653ab3410421cf7a21314c1af3ac5311036
BLAKE2b-256 checksum
How to use checksums
d15eb515e49dd4eace78b976f0933c4c4fe7a07dc0e8517afa38836d59a31d39
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Release files / fcgisgi-0.2.3-py3-none-any.whl

Download URL fcgisgi-0.2.3-py3-none-any.whl
Size 17.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
afe5a54c5fbbcdb21dfc9e4e8d872d638d989472fc32fb8885f11a789982f534
BLAKE2b-256 checksum
How to use checksums
af6400eafe58c6660931b555326e9f273d60855cf470b693cfdc4f217b616897
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Release history Release notifications | RSS feed

This release

0.2.3 This release

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page