WordPressブログ用のレンタルサーバーとして人気の「ColorfulBox(カラフルボックス)」ですが、実はPythonのWebアプリケーションもデプロイできます。

今回、Microsoft製のファイル変換ライブラリ「MarkItDown」を使った変換ツールをColorfulBox上に構築したので、その手順と踏んだ地雷をすべて共有します。

構築したもの:

  • フロントエンド:HTML/JavaScript(WordPress固定ページ)
  • バックエンド:Python FastAPI + MarkItDown
  • 機能:PowerPoint、Word、PDFなどをMarkdownに変換するWebツール
pptx/docx/pdf → Markdown 変換ツール pptx・docx・xlsx・pdf・html・csvなどのファイルをドラッグ&ドロップするだけでMarkdown形式に変換。Microsoft MarkItDownを使用したオンライン変換ツール。

    環境概要

    項目 内容
    サーバー ColorfulBox 共用サーバー(cPanel / LiteSpeed + Passenger)
    Python 3.11
    フレームワーク FastAPI
    ASGIサーバー uvicorn
    ポート 8001(ローカル)

    前提知識:なぜColorfulBoxでFastAPIは難しいのか

    ColorfulBoxのcPanelには「Setup Python App」という機能があり、一見簡単にPythonアプリを動かせそうに見えます。

    しかし、この機能はPassengerというWSGI向けの仕組みを使っており、FastAPIのようなASGIアプリケーションには対応していません

    そのため、以下のような構成を取る必要があります:

    ブラウザ → LiteSpeed → .htaccess(プロキシ) → uvicorn → FastAPI
    

    Passengerは「uvicornを起動するためのブートストラップ」として使い、実際のリクエスト処理はuvicornが担当します。


    Step 1: cPanelでPython環境をセットアップ

    1-1. Setup Python Appを開く

    cPanelにログインし、「ソフトウェア」→「Setup Python App」をクリックします。

    1-2. CREATE APPLICATIONをクリック

    1-3. 設定を入力

    項目 設定値 説明
    Python version 3.11.x 最新の安定版を推奨
    Application root api/markitdown Pythonファイルを置くディレクトリ
    Application URL ドメインapi/markitdown 公開URL
    Application startup file passenger_wsgi.py 起動ファイル
    Application Entry point application WSGI callable名

    「CREATE」をクリックして完了です。

    作成後、以下のパスが自動的に設定されます:

    • アプリルート:/home/(ユーザー名)/api/markitdown/
    • 仮想環境:/home/(ユーザー名)/virtualenv/api/markitdown/3.11/

    Step 2: Pythonファイルをアップロード

    以下の3ファイルを /home/(ユーザー名)/api/markitdown/ にアップロードします。

    main.py(FastAPIアプリ本体)

    """
    MarkItDown Converter API
    """
    
    import os
    import tempfile
    from fastapi import FastAPI, File, UploadFile
    from fastapi.middleware.cors import CORSMiddleware
    from fastapi.responses import JSONResponse
    from markitdown import MarkItDown
    
    app = FastAPI(title="MarkItDown Converter API", version="1.0.0")
    
    # CORS設定
    app.add_middleware(
        CORSMiddleware,
        allow_origins=["https://your-domain.jp"],
        allow_credentials=True,
        allow_methods=["POST", "GET", "OPTIONS"],
        allow_headers=["*"],
    )
    
    md = MarkItDown(enable_plugins=False)
    MAX_FILE_SIZE = 50 * 1024 * 1024  # 50MB
    
    @app.get("/api/health")
    async def health_check():
        return {"status": "ok", "service": "markitdown-converter"}
    
    @app.post("/api/markitdown/convert")
    async def convert_file(file: UploadFile = File(...)):
        temp_path = None
        try:
            filename = file.filename or "unknown"
            _, ext = os.path.splitext(filename)
            
            content = await file.read()
            if len(content) > MAX_FILE_SIZE:
                return JSONResponse(status_code=413, content={"success": False, "error": "ファイルサイズが50MBを超えています"})
            
            with tempfile.NamedTemporaryFile(delete=False, suffix=ext) as temp_file:
                temp_file.write(content)
                temp_path = temp_file.name
            
            result = md.convert(temp_path)
            
            return JSONResponse(status_code=200, content={"success": True, "markdown": result.text_content, "filename": filename})
        except Exception as e:
            return JSONResponse(status_code=500, content={"success": False, "error": f"変換エラー: {str(e)}"})
        finally:
            if temp_path and os.path.exists(temp_path):
                os.unlink(temp_path)
    

    passenger_wsgi.py(Passenger用ダミーファイル)

    """
    Passenger用エントリーポイント
    uvicornは別プロセスで起動するため、ここではダミーレスポンスを返すのみ
    """
    
    def application(environ, start_response):
        status = "200 OK"
        headers = [("Content-Type", "text/plain; charset=utf-8")]
        body = b"MarkItDown API is running."
        start_response(status, headers)
        return [body]
    

    requirements.txt

    fastapi>=0.109.0
    uvicorn>=0.27.0
    markitdown[all]>=0.1.0
    python-multipart>=0.0.6
    

    Step 3: 依存パッケージをインストール

    cPanelの「詳細」→「ターミナル」を開き、以下を実行します:

    # 既存の不要なプロセスを終了させる
    pkill -u peysrwib -f uvicorn
    
    # 仮想環境を有効化
    source /home/(ユーザー名)/virtualenv/api/markitdown/3.11/bin/activate
    
    # アプリディレクトリに移動
    cd /home/(ユーザー名)/api/markitdown
    
    # パッケージをインストール
    pip install -r requirements.txt
    

    markitdown[all] には多くの依存関係があるため、インストールには数分かかります。


    Step 4: .htaccessにプロキシ設定を追加

    /public_html/api/markitdown/.htaccess を編集し、以下を追記します:

    # uvicornへのプロキシ
    <IfModule mod_rewrite.c>
      RewriteEngine On
      RewriteCond %{REQUEST_URI} ^/api/markitdown/convert [NC]
      RewriteRule ^convert$ http://127.0.0.1:8001/api/markitdown/convert [P,L]
      RewriteCond %{REQUEST_URI} ^/api/markitdown/health [NC]
      RewriteRule ^health$ http://127.0.0.1:8001/api/markitdown/health [P,L]
    </IfModule>
    

    重要: cPanelが自動生成したPassenger設定(# DO NOT REMOVEで始まる部分)は削除しないでください


    Step 5: uvicornを起動

    # 既存の不要なプロセスを終了させる
    pkill -u peysrwib -f uvicorn
    
    # 仮想環境を有効化
    source /home/(ユーザー名)/virtualenv/api/markitdown/3.11/bin/activate
    
    # アプリディレクトリに移動
    cd /home/(ユーザー名)/api/markitdown
    
    # uvicornをバックグラウンドで起動
    nohup uvicorn main:app --host 127.0.0.1 --port 8001 > uvicorn.log 2>&1 &
    

    動作確認

    curl http://127.0.0.1:8001/api/markitdown/health
    

    {"status":"ok","service":"markitdown-converter"} が返れば成功です!


    踏んだ地雷と解決策

    地雷1: PassengerはASGIに対応していない

    症状: FastAPIアプリが動かない

    解決: uvicornを別プロセスで起動し、.htaccessでプロキシ転送する構成に変更

    地雷2: プロキシのパスが間違っている

    症状: {"detail":"Not Found"} が返る

    解決: .htaccessのRewriteRuleで、転送先のパスを正確に指定

    # ❌ 間違い
    RewriteRule ^api/markitdown/(.*)$ http://127.0.0.1:8001/api/$1 [P,L]
    
    # ✅ 正しい
    RewriteRule ^convert$ http://127.0.0.1:8001/api/markitdown/convert [P,L]
    

    地雷3: ポート番号の不一致

    症状: 「サーバーとの通信に失敗しました」

    解決: uvicornの起動ポートと.htaccessのプロキシ先ポートを一致させる

    地雷4: --workers 2でリソース上限に達する

    症状: 「リソースが一時的に利用できません」エラー

    原因: ColorfulBoxの共用サーバーにはプロセス数の上限があります。--workers 2 以上を指定すると、上限に引っかかってエラーになることがあります。

    解決: ワーカー数を指定しない(デフォルトは1)か、明示的に --workers 1 を指定

    # ❌ 問題が起きる可能性
    nohup uvicorn main:app --host 127.0.0.1 --port 8001 --workers 2 > uvicorn.log 2>&1 &
    
    # ✅ 推奨
    nohup uvicorn main:app --host 127.0.0.1 --port 8001 > uvicorn.log 2>&1 &
    

    地雷5: ffmpegがインストールされていない

    症状: 起動時に Couldn't find ffmpeg or avconv の警告

    影響: 音声ファイル(.mp3, .wav)の変換ができない

    対策: 共用サーバーではffmpegをインストールできないため、音声変換機能は諦める。PowerPoint、Word、PDF、Excelの変換には影響なし。


    運用上の注意点

    uvicornの再起動が必要なケース

    状況 再起動が必要?
    通常のアクセス ❌ 不要
    サーバー再起動・メンテナンス後 ✅ 必要
    uvicornがクラッシュした場合 ✅ 必要
    コード更新時 ✅ 必要

    再起動コマンド(ワンライナー)

    source /home/(ユーザー名)/virtualenv/api/markitdown/3.11/bin/activate && 
    cd /home/(ユーザー名)/api/markitdown && 
    pkill -u ユーザー名 -f "uvicorn main:app.*8001" 2>/dev/null; 
    nohup uvicorn main:app --host 127.0.0.1 --port 8001 > uvicorn.log 2>&1 &
    

    死活監視スクリプト(オプション)

    cronで定期的にuvicornの状態をチェックし、落ちていたら自動で再起動するスクリプトを設定することも可能です。


    まとめ

    ColorfulBoxの共用サーバーでFastAPIを動かすのは、VPSほど自由度はありませんが、工夫次第で十分実用的なWebアプリを公開できます。

    ポイント:

    1. PassengerはASGI非対応なので、uvicornを別プロセスで起動
    2. .htaccessでプロキシ転送の設定を行う
    3. ワーカー数は1に抑える(共用サーバーのプロセス数上限対策)
    4. サーバー再起動後はuvicornの手動起動が必要

    個人開発者にとって、月額数百円のレンタルサーバーでPython APIを動かせるのは大きなメリットです。試してみてください。


    参考リンク