2026年7月19日
2026年7月19日
Traefik で接続できない場合の対処:404・502・証明書・WebSocketの切り分け
Traefik 経由でサイトやアプリに接続できないときは、まず返ってくる応答の種類を確認すると原因をかなり絞り込めます。「404 page not found」ならルーターが一致していない、「502 Bad Gateway」ならバックエンドに届いていない、証明書エラーなら証明書の取得に失敗している、WebSocket だけが切れるなら途中のミドルウェアやタイムアウト、という見当がつきます。この記事では Docker で Traefik v3 を動かす構成を例に、原因を起きやすい順に確認していく手順を説明します。
Traefik は設定項目や既定値がバージョンごとに見直されています。ここでの記述は(2026年10月現在)の v3 系を前提にしているため、v2 から移行した環境では公式の移行ガイドもあわせて確認してください。
最初にやること:ログとダッシュボードで状況を見る
設定を触る前に、Traefik が何を認識しているかを確認します。
# Traefik コンテナのログ(エラーの有無を確認)
docker logs --tail 100 traefik
# 一時的にログを詳しくする場合は、静的設定に --log.level=DEBUG を加えて再起動する
ダッシュボード(--api.dashboard=true)を有効にすると、ルーター・サービス・ミドルウェアの一覧と、それぞれのエラー表示を確認できます。検証用に --api.insecure=true で 8080 番ポートから開く方法もありますが、認証なしで公開されるため、本番では使わず、ルーターと認証ミドルウェアを付けて公開してください。API が開いていれば、次のように登録済みのルーターを一覧できます。
curl -s http://localhost:8080/api/http/routers | python3 -m json.tool
目的のルーターが一覧にない場合は原因1へ、あるのにエラーになる場合は原因2以降へ進みます。
原因1:ルーターが作られていない・一致していない(404)
Traefik が「404 page not found」というそっけない文字列を返すときは、リクエストに一致するルーターがありません。
よくある理由
--providers.docker.exposedbydefault=falseにしているのに、対象コンテナにtraefik.enable=trueのラベルがないHost()ルールのドメイン名の綴りが違う、またはバッククォートで囲んでいない- ルーターの
entrypointsが、実際にアクセスしているポート(80 か 443 か)と合っていない - ラベルを変更したのにコンテナを再作成していない(
docker compose up -dで再作成する)
最小構成の例を示します。ドメイン名とメールアドレスは自分の環境に置き換えてください。
services:
traefik:
image: traefik:v3 # 本番ではマイナーバージョンまで固定するのがおすすめ
command:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --certificatesresolvers.le.acme.httpchallenge=true
- --certificatesresolvers.le.acme.httpchallenge.entrypoint=web
- --certificatesresolvers.le.acme.email=admin@example.com
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
networks:
- proxy
wordpress:
image: wordpress
labels:
- traefik.enable=true
- traefik.http.routers.wp.rule=Host(`www.example.com`)
- traefik.http.routers.wp.entrypoints=websecure
- traefik.http.routers.wp.tls.certresolver=le
- traefik.http.services.wp.loadbalancer.server.port=80
networks:
- proxy
networks:
proxy:
Docker ソケットをマウントすると Traefik がホストの Docker を操作できる権限を持つため、読み取り専用でマウントし、Traefik のダッシュボードを外部に開放しないようにします。
原因2:バックエンドに届かない(502・504)
ルーターは一致しているのに「Bad Gateway」になる場合、Traefik からコンテナへの接続に失敗しています。
- ポートの指定ミス:アプリが 3000 番で待ち受けているのに、Traefik が 80 番へ接続している。
traefik.http.services.<名前>.loadbalancer.server.portを待ち受けポートに合わせます。コンテナが複数のポートを公開しているときは、この指定がないと意図しないポートが選ばれることがあります。 - ネットワークが違う:Traefik とアプリのコンテナが同じ Docker ネットワークにいない。複数のネットワークに参加しているコンテナでは、
traefik.docker.network=proxyのようにどのネットワークを使うかを指定します。 - アプリが起動していない・localhost だけで待ち受けている:コンテナ内のアプリが
127.0.0.1だけで待ち受けていると、Traefik から接続できません。0.0.0.0で待ち受けるよう設定します。
確認には、Traefik のコンテナからバックエンドへ直接接続してみるのが早道です。
docker exec -it traefik wget -qO- -S http://wordpress:80/ 2>&1 | head -5
504 の場合は、バックエンドの応答が遅すぎてタイムアウトしています。アプリ側の処理時間を先に確認し、必要な場合だけタイムアウトを延ばします。
原因3:証明書が取得できない(証明書エラー)
ブラウザに証明書エラーが出て、証明書の発行者が「TRAEFIK DEFAULT CERT」になっている場合、Let's Encrypt からの証明書取得に失敗し、Traefik の自己署名証明書が使われています。
- HTTP チャレンジでは、外部から 80 番ポートへ到達できる必要がある。ファイアウォールやクラウドのセキュリティグループで 80 番を閉じていないか確認する
- DNS がまだこのサーバーを向いていない
acme.jsonの権限が緩いと Traefik が読み込みを拒否するため、chmod 600にする- 何度も失敗を繰り返すと Let's Encrypt のレート制限にかかるため、検証中はステージング環境の CA サーバー(
--certificatesresolvers.le.acme.caserverで指定)を使う
ログに ACME 関連のエラーが出ているので、内容を読めばどれに当たるかが分かります。
原因4:リダイレクトループ・HTTPSの判定ずれ
WordPress を Traefik の後ろに置くと、Traefik とコンテナの間は HTTP のため、WordPress が HTTP で表示されていると判断し、リダイレクトループや混在コンテンツを起こすことがあります。Traefik は X-Forwarded-Proto ヘッダーを付けて転送するので、wp-config.php の require_once ABSPATH . 'wp-settings.php'; より前で次のように判定します。
// Traefik などのリバースプロキシ経由で HTTPS アクセスされた場合に WordPress へ伝える
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] )
&& 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
$_SERVER['HTTPS'] = 'on';
}
Traefik の前にさらに Cloudflare などのプロキシがある場合、Traefik は既定では外部から届いた転送ヘッダーを信頼しません。エントリーポイントの forwardedHeaders.trustedIPs に前段のプロキシのアドレス範囲を指定して、信頼する送信元を限定します。
原因5:WebSocketだけがつながらない・切れる
Traefik は WebSocket を標準で中継できるため、通常は追加の設定は要りません。通常の HTTP は動くのに WebSocket だけが失敗する場合は、次を確認します。
- ハンドシェイクが成功しているか:次のコマンドで
101 Switching Protocolsが返るかを見ます。400 や 404 が返るなら、ルールのパスや認証ミドルウェアがアップグレード要求を拒否しています。
curl -i -N --http1.1 \
-H "Connection: Upgrade" -H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
https://ws.example.com/socket
- ミドルウェアの影響:リクエストを読み込んでから転送するタイプのミドルウェア(バッファリングなど)を WebSocket のルーターに付けると、接続が確立しないことがあります。WebSocket 用のパスは専用のルーターに分け、不要なミドルウェアを外します。
- タイムアウト:一定時間で切れる場合は、エントリーポイントの
transport.respondingTimeoutsの値や、前段のロードバランサー・CDN のアイドルタイムアウトを確認します。Traefik の既定値はバージョンによって変わっているため、使用中のバージョンの公式ドキュメントで確認し、アプリ側で定期的に ping を送る設計とあわせて調整します。 - 前段のサービス:Cloudflare などを経由している場合、そのサービス側で WebSocket が許可されているかも確認します。
作業時の注意
設定変更の前に docker-compose.yml と acme.json をバックアップし、可能であれば検証用のサブドメインで試してから本番のルーターに反映します。
まとめ
- 404 はルーター不一致、502 はポートかネットワーク、証明書エラーは ACME の失敗、という対応で切り分ける
- 最初にログとダッシュボード(API)で、Traefik が認識しているルーターを確認する
- WordPress では
X-Forwarded-Protoを使った HTTPS 判定と、前段プロキシの信頼設定がつまずきどころ - WebSocket は
101が返るかを確かめ、ミドルウェアとタイムアウトを順に見直す