6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

さくらのレンタルサーバで Litestar を CGI / mod_wsgi で動かした記録

Last updated at Posted at 2025-12-13

さくらのレンタルサーバで Litestar を CGI / mod_wsgi で動かした記録

TL;DR: suexec 付き共用サーバーでも、pyenv + a2wsgi を組み合わせれば Litestar (ASGI) を CGI / mod_wsgi で動かせる。非同期や WebSocket は諦めて、小規模 API に割り切ると幸せ。

Qiita 用に、試行錯誤の結果と設定例をまとめました。~/www/ をルートにしたシンプルな構成で進めます。

前提と環境

  • さくらインターネット共用サーバー (FreeBSD 13.x、suexec 有効)
  • Python 3.12.6 を pyenv でユーザー領域にインストール
  • 依存ライブラリ: litestar, a2wsgi, sniffio
  • ドキュメントルート: ~/www/
/home/enujii/
├── www/
│   ├── .htaccess
│   ├── app.py
│   └── index.cgi
└── log/

Step 1. pyenv + OpenSSL で Python 3.12.6 を用意

共有サーバーの Python は古いので、自前でビルド。

opensslのリリースは下記を参考に。

mkdir -p ~/local/src && cd ~/local/src
curl -L https://github.com/openssl/openssl/releases/download/openssl-3.6.0/openssl-3.6.0.tar.gz -o openssl-3.6.0.tar.gz
tar xvfpz openssl-3.6.0.tar.gz

git clone https://github.com/pyenv/pyenv.git ~/.pyenv
cat <<'EOF' >> ~/.bashrc
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
export TMPDIR=$HOME/tmp
eval "$(pyenv init -)"
EOF
source ~/.bashrc

mkdir -p ~/local/openssl/3.6.0 && cd ~/local/src/openssl-3.6.0
./Configure --prefix=$HOME/local/openssl/3.6.0
make install_sw

CONFIGURE_OPTS="--with-openssl=$HOME/local/openssl/3.6.0" \
PY_UNSUPPORTED_OPENSSL_BUILD=static \
TMPDIR="$HOME/tmp" \
pyenv install 3.12.6
pyenv global 3.12.6

pip install --upgrade pip
pip install litestar a2wsgi sniffio

Step 2. Litestar アプリ (~/www/app.py)

from litestar import Litestar, get


@get("/", sync_to_thread=False)
def hello_world() -> dict[str, str]:
    return {"message": "Hello, Litestar on Sakura CGI!"}


@get("/api", sync_to_thread=False)
def api() -> dict[str, str]:
    return {"message": "Hello, API on Sakura CGI!"}


app = Litestar(route_handlers=[hello_world, api])

Step 3. CGI エントリ (~/www/index.cgi)

#!/home/enujii/.pyenv/versions/3.12.6/bin/python
"""CGI entry point wrapping Litestar via a2wsgi."""
from a2wsgi import ASGIMiddleware
from wsgiref.handlers import CGIHandler

from app import app

wsgi_app = ASGIMiddleware(app)

if __name__ == "__main__":
    CGIHandler().run(wsgi_app)

Step 4. .htaccess 設定

# ~/www/.htaccess
Options +ExecCGI
AddHandler cgi-script .cgi .py
DirectoryIndex index.cgi index.html index.htm

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.cgi/$1 [QSA,L]

<FilesMatch "\.py$">
  Require all denied
</FilesMatch>

サブディレクトリで使う場合は RewriteRule を相対パス (index.cgi/$1) に。@docs/hosting/sakura-cgi/src.htaccess 参照。

Step 5. パーミッション (suexec 対策)

chmod 755 ~/www ~/www/src
chmod 755 ~/www/index.cgi
chmod 644 ~/www/.htaccess ~/www/app.py

Home 配下 / 所有者本人 / 755 (ディレクトリ・CGI) が必須。shim (~/.pyenv/shims/python) は使わないこと。

Step 6. ハマりポイント & 解決

エラー 原因 解決
Request exceeded the limit of 10 internal redirects .htaccess の Rewrite が /index.cgi のような絶対パス ルート・サブディレクトリとも index.cgi/$1 に変更
suexec policy violation shebang がホーム外 / 700 パーミッション / shim 利用 #!/home/.../python に固定、chmod 755 ~/www & index.cgi
ModuleNotFoundError: sniffio Litestar 依存不足 pip install sniffio
DeprecationWarning: cgitb + Response header name '<!--' cgitb が HTML を吐いてヘッダー壊す cgitb を削除し、ログでスタックトレース確認

Step 7. デバッグ Tips

  • エラーログはコントロールパネルから取得(~/log/error_log には無い, 0時にサーバー反映っぽい)。
  • tail -f が使えないので、アクセス直後にログを再ダウンロード。
  • suexec の詳細は「スクリプトログ」で確認。
  • GitHub リリースアセットは curl -L を忘れず(0バイト tar の罠)。

Step 8. まとめ

  1. pyenv + OpenSSL で最新 Python を用意。
  2. Litestar をシンプルな JSON API として実装し、sync_to_thread=False で警告回避。
  3. a2wsgi で ASGI → WSGI に変換して CGI 経由で実行。
  4. .htaccess / 権限 / shebang を suexec の要件に合わせる。
  5. エラーログはコントロールパネルで確認。

実際にハマったところ & 感想

  • ~/www/src/ に置いたままでは動かなかった: suexec が ~/www/index.cgi を期待しているため、サブディレクトリ経由の Rewrite が想定以上に複雑化。何度も Rewrite → リダイレクト地獄に陥り、最終的に「ルート直下に集約する」方針に落ち着いた。
  • ログがすぐには見つからない: ~/log/error_log には何も出ず、コントロールパネルで毎回ダウンロードする必要があることに気付くまで時間を浪費。
  • suexec policy violation の連発で心が折れかけた。shebang / パーミッション / 所有者 / ディレクトリ階層がすべて揃って初めて実行される、と体で覚える結果に。
  • 失敗を繰り返したことで、共用サーバーの制約を丁寧に一つずつ潰す大切さ (そして大変さ) を痛感した。

基本的に「できるだけシンプルに」「ルート直下で完結させる」ことが成功への近道でした。

付録: Apache + mod_wsgi + a2wsgi で Litestar を動かす場合

a2wsgi を WSGI スクリプトで使えば、mod_wsgi しかない環境でも Litestar を動かせます。ただし非同期の利点は失われるため、小規模 API のみ推奨。

# wsgi.py
import os
import sys

sys.path.insert(0, os.path.dirname(__file__))

from a2wsgi import ASGIMiddleware
from app import app

wsgi_app = ASGIMiddleware(app)
application = wsgi_app

Apache 設定例:

WSGIDaemonProcess mylitestar python-home=/home/enujii/.pyenv/versions/3.12.6
WSGIProcessGroup mylitestar
WSGIScriptAlias / /home/enujii/www/wsgi.py

<Directory /home/enujii/www>
    Require all granted
</Directory>

注意点

  • 非同期 I/O や WebSocket は基本的に不可。
  • ラッパーのオーバーヘッドでパフォーマンスは ASGI サーバーに劣る。
  • 共有環境では mod_wsgi の設定を細かく変えられないことも多い。

代替案 (推奨)

Client → Apache / Nginx (リバースプロキシ) → ASGI サーバー (uvicorn / hypercorn) → Litestar

ASGI の強みをフルに活かすならこちらの構成を検討してください。a2wsgi + mod_wsgi は「どうしても WSGI 環境しかない」場合の最終手段です。

6
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?