はじめに
SinkCapitalではメンバーのスキルデータベース化を進めることで、プロジェクトとメンバーのマッチング精度をより高める取り組みを行なっています。その取り組みの中でstreamlitを用いた可視化を行なっています。

 Streamlitはデータアプリケーションを手軽に作成できるフレームワークですが、デフォルトの設定では<head>タグをカスタマイズできず、Googleタグマネージャー(GTM)を挿入することができません。本記事では、Cloud Run上でStreamlitを動かしながらGTMを実装する方法について解説します。
Streamlitのヘッダー制約
 StreamlitはHTMLのカスタマイズが制限されており、直接<head>タグにスクリプトを挿入することができません。そのため、通常の方法ではGTMを適用できないという課題があります。Cloud Run上でNginxをリバースプロキシとして設定し、レスポンスのヘッダーを書き換えることでGTMを挿入することができます。
1. Nginxの設定
 以下のようなNginxの設定ファイル(nginx.conf)を用意します。
worker_processes auto;
events {
    worker_connections 4096;
}
http {
    include mime.types;
    # ✅ `gzip` をオフにして `sub_filter` を確実に適用
    gzip off;
    ...
    server {
        listen 80;
        location / {
            ...
            # ✅ `gzip` 圧縮を無効化(sub_filter が適用されるように)
            proxy_set_header Accept-Encoding "";
            # ✅ `sub_filter` の設定
            sub_filter_types text/html;  # `application/json` などは不要
            sub_filter "<title>Streamlit</title>" "<title>Streamlit</title><!-- Google Tag Manager --><script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!=='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','GTM-XXXXXXXX');</script><!-- End Google Tag Manager -->";
            sub_filter "</body>" "<script>window.dataLayer = window.dataLayer || [];window.dataLayer.push({'event': 'page_view', 'page_path': '$request_uri'});</script></body>";
            sub_filter_once off;
        }
        ...
    }
}
この設定により、Streamlitのレスポンスの</head>タグ直前にGTMのスクリプトを挿入することができます。
2. Dockerfileの作成
次に、NginxとStreamlitを含むDockerfileを作成します。
# ベースイメージにPython 3.12を使用
FROM python:3.12-slim
# 環境変数を設定
ENV PYTHONDONTWRITEBYTECODE=1 \
    PIP_NO_CACHE_DIR=off \
    POETRY_HOME="/opt/poetry"
# PATHにPoetryのインストールディレクトリを追加
ENV PATH="${POETRY_HOME}/bin:${PATH}"
# 必要なシステムパッケージをインストール
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl \
    build-essential \
    nginx \
    && rm -rf /var/lib/apt/lists/*
# Poetryをインストール
RUN curl -sSL https://install.python-poetry.org | python -
# Poetryの仮想環境作成を無効化
RUN poetry config virtualenvs.create false
# 作業ディレクトリの設定
WORKDIR /app
RUN pip install --upgrade poetry
# アプリケーションコードをコピー
COPY pyproject.toml /app
COPY poetry.lock /app
COPY README.md /app
# 依存関係をインストール
RUN poetry install --no-interaction --no-root --without dev --no-ansi
# アプリケーションコードをコピー
COPY . /app
# Streamlit の設定ファイルを作成
RUN mkdir -p ~/.streamlit && \
    echo "[server]\n\
    enableCORS = false\n\
    runOnSave = true\n\
    headless = true\n\
    port = 8080\n\
    " > ~/.streamlit/config.toml
# Nginx の設定を追加
COPY nginx.conf /etc/nginx/nginx.conf
# 📌 `custom_502.html` を Nginx の `html` フォルダにコピー(重要)
COPY custom_502.html /usr/share/nginx/html/custom_502.html
# 必要なポートを公開
EXPOSE 80
# Nginx と Streamlit を両方起動
CMD service nginx start && \
    streamlit run main.py --server.port=8080 --server.address=127.0.0.1 --server.enableXsrfProtection=false
Cloud Runのコールドスタート対応
Cloud Runはコールドスタート時に503エラーを返すことがあります。これを回避するために、初回リクエスト時に502ページを表示し、バックエンドの準備ができたらリダイレクトする仕組みを作ります。
1. Nginxの502エラーページを準備
以下のように502エラーページを作成し、Cloud Runのコールドスタート時に表示させます。
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>サービス起動中...</title>
    <script>
        setTimeout(function() {
            location.reload();
        }, 1000); // 1秒ごとにリロード
    </script>
    <style>
        body {
            text-align: center;
            font-family: Arial, sans-serif;
            margin-top: 50px;
        }
    </style>
</head>
<body>
    <h1>サービスを起動中...</h1>
    <p>しばらくお待ちください。自動的にリロードされます。</p>
</body>
</html>
2. Nginxの設定を変更
Nginxの設定に、502エラーページを適用する設定を追加します。
worker_processes auto;
events {
    worker_connections 4096;
}
http {
    ...
    server {
        listen 80;
        location / {
            ...
            
            error_page 502 /custom_502.html;
            location = /custom_502.html {
                root /usr/share/nginx/html;
            }
            ...
        }
        ...
    }
}
この設定により、Cloud Runのコールドスタート時には502ページを表示し、サービスが起動すると自動でStreamlitに接続されるようになります。
まとめ
本記事では、
- Streamlitではヘッダーを直接カスタムできないため、GTMを適用できない
 - Nginxをリバースプロキシとして挟むことでGTMを適用可能
 - Cloud Runのコールドスタート時には502エラーページを表示し、エラーを回避する
 
という方法について解説しました。Cloud Run上でStreamlitを運用しながらGTMを導入する際に、ぜひ参考にしてください。