We have developed a unox sockets configuration for py4web I hope this helps someone

16 views
Skip to first unread message

Davidiam

unread,
May 22, 2026, 11:59:34 AM (2 days ago) May 22
to py4web
Overview:

We wanted to use UNIX sockets for py4web instead of ports and we finally developed a configuration for this that works.  Unix sockets are more secure and for us were much easier as we didn't need to manage our port ranges and use iptables.

The main difficulty we had was the _dashboard application which is an SPA application.  It want to be served from the root, but we needed a URL prefix since we had multiple py4web instances from the same host.  For this reason it was necessary to create a custom wsgi.  I couldn't find any url_prefix options which i could use with gunicorn and sockets.  If such an option exists then please let me know.  AI told us this wasn't possible.

Our configuration is webseal/trustbuilder => Apache reverse proxy on Linux and py4web running gunicorn.  

After a lot of trial and error, we came up with a working configuration.

I am sharing the basic structure for others who may want to use sockets with py4web:
Note: Our py4web is conda installed and you will need to install gunicorn in your environment.

This is the wsgi file contents:

from py4web.core import wsgi
APPS_FOLDER = "<path_to_py4web>/apps"
PREFIX = "<URL_prefix>"
from py4web.core import wsgi
app = wsgi(
    apps_folder="<path_to_py4web>/apps",
    password_file="<path_to_py4web>/password.txt",
    dashboard_mode="full"   # Very important!!
)

def application(environ, start_response):
    raw_path = environ.get("PATH_INFO") or "/"
    print("RAW PATH:", raw_path, flush=True)
    if raw_path.startswith(PREFIX):
        new_path = raw_path[len(PREFIX):] or "/"
    else:
        new_path = raw_path
    if not new_path.startswith("/"):
        new_path = "/" + new_path
    environ["SCRIPT_NAME"] = PREFIX
    environ["PATH_INFO"] = new_path
    print("FINAL:", environ["SCRIPT_NAME"], environ["PATH_INFO"], flush=True)
    return app(environ, start_response)

This is the apache reverse proxy configuration:

AllowEncodedSlashes NoDecode

# -------------------------------
# Logging
# -------------------------------
SetEnvIf Request_URI "^<url_prefix>" log_py4web
CustomLog <path_to_log_file>/p4web-access.log combined env=log_py4web

SetEnvIf Request_URI "^<url_prefix>" log_py4web
CustomLog <path_to_log>/p4web-access.log combined env=log_py4web

# =========================================================
# ROOT passthrough (REQUIRED for _dashboard SPA)
# =========================================================
ProxyPass "/_dashboard/" \
  "unix:<path_to_sockets_dir>/py4web.sock|http://<ip apache is listening on>/_dashboard/"

ProxyPassReverse "/_dashboard/" \
  "unix:<path_to_sockets_dir>/py4web.sock|http://<ip apache is listening on>/_dashboard/"

# =========================================================
# ROOT passthrough (REQUIRED for _dashboard SPA)
# =========================================================
ProxyPass "/static/" \
  "unix:<path_to_sockets_dir>/py4web.sock|http://<ip apache is listening on>/static/"

ProxyPassReverse "/static/" \
  "unix:<path_to_sockets_dir>/py4web.sock|http://<ip apache is listening on>/static/"

# =========================================================
# Non-SPA PY4WEB APPS
# =========================================================
ProxyPass "<url_prefix>" \
  "unix:<path_to_sockets_dir>/py4web.sock|http://<ip apache is listening on>/"

ProxyPassReverse "<url_prefix>" \
  "unix:<path_to_sockets_dir>/py4web.sock|http://<ip apache is listening on>/"

To run this with gunicorn from the command-line:

gunicorn my_wsgiwrapper:application \
  --bind unix:<path_to_sockets>/py4web.sock \
  --workers 1 \
  --log-level debug \
  --capture-output \
  --timeout 120
Reply all
Reply to author
Forward
0 new messages