**JWTトークンをデータベースに保存しようとしたら「値が長すぎる」エラー**——この問題に遭遇したことはありませんか?
原因は単純ですが、意外と見落としがちなポイントです。
この記事では、**JWTトークンのカラムサイズ問題**の原因と、WordPressプラグインでの**自動マイグレーションによる解決方法**を実装コード付きで紹介します。
## こんな人におすすめ
- WordPressプラグインでJWTトークンを扱う開発者
- DBカラムサイズ起因のエラーに悩んでいる方
- 既存テーブルの自動マイグレーション実装を検討している方
- 本番環境でのみ発生する謎のエラーを調査中の方
## 目次
- [背景:WordPressプラグインでJWT認証を実装](#背景wordpressプラグインでjwt認証を実装)
- [エラー内容:「値が長すぎる」データベースエラー](#エラー内容値が長すぎるデータベースエラー)
- [原因:JWTトークンの実際の長さとvarchar(64)の限界](#原因jwtトークンの実際の長さとvarchar64の限界)
- [解決方法:カラムサイズ変更と自動マイグレーション](#解決方法カラムサイズ変更と自動マイグレーション)
- [学んだこと](#学んだこと)
- [動作確認の手順](#動作確認の手順)
- [改善結果](#改善結果)
- [よくある質問(FAQ)](#よくある質問faq)
- [まとめ](#まとめ)
## 背景:WordPressプラグインでJWT認証を実装
WordPressプラグインと外部の画像生成サービスを連携させるプロジェクトで、SSOログイン機能を実装しました。
ユーザーがWordPress側で認証済みであれば、外部サービスでも再ログインなしで利用できる仕組みです。
この認証連携にJWT形式のPermit Tokenを使用し、トークンをデータベースに保存する設計としました。
```mermaid
sequenceDiagram
participant User as ユーザー
participant WP as WordPress
participant DB as データベース
participant Ext as 外部サービス
User->>WP: ログイン済み状態でアクセス
WP->>WP: JWTトークン生成
WP->>DB: トークンを保存
Note over DB: ❌ varchar(64)では<br/>保存できない!
WP-->>User: エラー発生
```
ところが本番環境で画像生成時にエラーが発生。
正直、最初は何が原因か分からず焦りました。
## エラー内容:「値が長すぎる」データベースエラー
本番環境で発生したエラーメッセージは以下の通りです。
```
Failed to create JWT permit token: WordPress データベースエラー:
次のフィールドの値の処理に失敗しました: token。
提供された値が長すぎるか、無効なデータを含んでいる可能性があります。
```
コンソールログでは `/api/usage/prepare` エンドポイントが500エラーを返していました。
## 原因:JWTトークンの実際の長さとvarchar(64)の限界
データベーステーブル定義で `token` カラムが `varchar(64)` に設定されていました。
しかし実際に生成されるJWTトークンは **179〜229バイト** 程度あり、約115〜165バイト不足していたのです。
この計算は後から考えると当然なのですが、実際にエラーに遭遇するまで気づきませんでした。
### JWTトークンの構造と長さの内訳
JWTトークンは3つのパートがドット(.)で連結された構造になっています。
| 構成要素 | エンコード | サイズ |
|---------|-----------|--------|
| ヘッダー | Base64url | 約36バイト |
| ペイロード | Base64url | 100〜150バイト |
| 署名(HS256) | Base64url | 約43バイト |
| ドット区切り | - | 2バイト |
| **合計** | - | **約181〜231バイト** |
つまり、**varchar(64)では半分以下しか格納できない**計算になります。
## 解決方法:カラムサイズ変更と自動マイグレーション
JWTトークンを正しく保存するための解決方法を、3つのステップで解説します。
### ステップ1:テーブルスキーマの修正
```php
// Before
token varchar(64) NOT NULL UNIQUE,
// After
token varchar(512) NOT NULL UNIQUE,
```
### ステップ2:既存テーブルの自動マイグレーション実装
プラグイン有効化時に自動的にカラムを拡張するメソッドを追加します。
実装コード:
```php
private function fix_token_column_length() {
global $wpdb;
// 現在のカラム定義を確認
$column_info = $wpdb->get_row(
"SHOW COLUMNS FROM {$this->table_name} WHERE Field = 'token'"
);
// varchar(64)の場合のみ拡張
if ($column_info && strpos($column_info->Type, 'varchar(64)') !== false) {
$result = $wpdb->query(
"ALTER TABLE {$this->table_name} MODIFY token varchar(512) NOT NULL"
);
if ($result !== false) {
error_log("Extended token column length in {$this->table_name}");
}
}
}
```
### ステップ3:コンストラクタでの呼び出し
```php
public function __construct() {
// ...
$this->maybe_create_table();
$this->fix_duplicate_indexes();
$this->fix_token_column_length(); // 追加
}
```
## 学んだこと
1. **JWTトークンの長さを事前に計算する**
JWT形式のトークンを保存する場合、ペイロードの内容によって長さが変動します。
十分な余裕を持ったカラムサイズを設定しておくことが重要です。
2. **自動マイグレーションパターン**
WordPressプラグインでは `dbDelta()` だけでなく、既存テーブルのカラム変更も考慮した自動マイグレーション機能が有効です。
3. **エラーメッセージの読み解き**
「値が長すぎる」というDBエラーは、カラムサイズと実際のデータ長の不一致を示唆しています。
4. **テスト環境との差異**
テスト環境ではマイグレーションがスキップされる設計になっていたため、本番で初めて問題が顕在化しました。
## 動作確認の手順
1. ローカル環境でプラグイン有効化後、テーブル構造確認: `DESCRIBE wp_permit_tokens`
2. `token` カラムが `varchar(512)` になっていることを確認
3. JWT Permit Token生成テスト: トークン長が181バイト程度で正常に保存されることを確認
4. 実際のサービスから画像生成テストを実行し、エラーが解消されていることを確認
## 改善結果
この修正により、以下の効果が得られました。
- **エラー発生率**: 100% → 0%
- **対応可能トークン長**: 64バイト → 512バイト(約8倍の余裕)
- **既存環境への影響**: 自動マイグレーションにより追加作業不要
特に自動マイグレーションパターンは、今後のスキーマ変更でも活用できそうです。
## よくある質問(FAQ)
### Q. JWTトークンの保存にはvarcharとtextどちらが適切?
**A. varchar(512)を推奨します。**
JWTトークンの長さは通常200〜300バイト程度で収まります。
varchar(512)なら十分な余裕があり、インデックスも作成可能です。
text型はインデックス作成に制限があるため、トークンの一意性チェックが必要な場合は避けた方が良いでしょう。
### Q. なぜvarchar(255)ではなくvarchar(512)にしたのか?
**A. ペイロードの拡張に備えるためです。**
JWTのペイロードにはユーザー情報やクレームを追加できます。
将来的にペイロードが増える可能性を考慮し、余裕を持ってvarchar(512)としました。
ストレージへの影響は微小なので、安全側に倒しています。
### Q. 既存データへの影響はある?
**A. ありません。**
`ALTER TABLE ... MODIFY`でカラムサイズを拡張する場合、既存データはそのまま保持されます。
縮小する場合はデータ切り詰めのリスクがありますが、拡張であれば安全です。
### Q. dbDelta()でカラムサイズ変更はできないのか?
**A. dbDelta()はカラムサイズ変更に対応していません。**
WordPressの`dbDelta()`関数は、新規カラム追加やインデックス作成には対応していますが、既存カラムの型変更には対応していません。
そのため、今回のような自動マイグレーション処理を別途実装する必要があります。
## まとめ
JWTトークンをデータベースに保存する際は、**トークンの実際の長さを事前に計算**することが重要です。
- JWTトークンは最低でも**180バイト以上**になる
- `varchar(64)`では**確実に不足**する
- **varchar(512)**程度を設定しておけば安心
- 既存テーブルには**自動マイグレーション**で対応可能
この問題は地味ですが、本番環境で突然発生すると焦ります。
事前に適切なカラムサイズを設計しておきましょう。
JWTトークンをデータベースに保存しようとしたら「値が長すぎる」エラー——この問題に遭遇したことはありませんか?
原因は単純ですが、意外と見落としがちなポイントです。
この記事では、JWTトークンのカラムサイズ問題の原因と、WordPressプラグインでの自動マイグレーションによる解決方法を実装コード付きで紹介します。
こんな人におすすめ
- WordPressプラグインでJWTトークンを扱う開発者
- DBカラムサイズ起因のエラーに悩んでいる方
- 既存テーブルの自動マイグレーション実装を検討している方
- 本番環境でのみ発生する謎のエラーを調査中の方
目次
背景:WordPressプラグインでJWT認証を実装
WordPressプラグインと外部の画像生成サービスを連携させるプロジェクトで、SSOログイン機能を実装しました。
ユーザーがWordPress側で認証済みであれば、外部サービスでも再ログインなしで利用できる仕組みです。
この認証連携にJWT形式のPermit Tokenを使用し、トークンをデータベースに保存する設計としました。
sequenceDiagram
participant User as ユーザー
participant WP as WordPress
participant DB as データベース
participant Ext as 外部サービス
User->>WP: ログイン済み状態でアクセス
WP->>WP: JWTトークン生成
WP->>DB: トークンを保存
Note over DB: ❌ varchar(64)では<br/>保存できない!
WP-->>User: エラー発生
ところが本番環境で画像生成時にエラーが発生。
正直、最初は何が原因か分からず焦りました。
エラー内容:「値が長すぎる」データベースエラー
本番環境で発生したエラーメッセージは以下の通りです。
Failed to create JWT permit token: WordPress データベースエラー:
次のフィールドの値の処理に失敗しました: token。
提供された値が長すぎるか、無効なデータを含んでいる可能性があります。
コンソールログでは /api/usage/prepare エンドポイントが500エラーを返していました。
原因:JWTトークンの実際の長さとvarchar(64)の限界
データベーステーブル定義で token カラムが varchar(64) に設定されていました。
しかし実際に生成されるJWTトークンは 179〜229バイト 程度あり、約115〜165バイト不足していたのです。
この計算は後から考えると当然なのですが、実際にエラーに遭遇するまで気づきませんでした。
JWTトークンの構造と長さの内訳
JWTトークンは3つのパートがドット(.)で連結された構造になっています。
| 構成要素 |
エンコード |
サイズ |
| ヘッダー |
Base64url |
約36バイト |
| ペイロード |
Base64url |
100〜150バイト |
| 署名(HS256) |
Base64url |
約43バイト |
| ドット区切り |
- |
2バイト |
| 合計 |
- |
約181〜231バイト |
つまり、varchar(64)では半分以下しか格納できない計算になります。
解決方法:カラムサイズ変更と自動マイグレーション
JWTトークンを正しく保存するための解決方法を、3つのステップで解説します。
ステップ1:テーブルスキーマの修正
// Before
token varchar(64) NOT NULL UNIQUE,
// After
token varchar(512) NOT NULL UNIQUE,
ステップ2:既存テーブルの自動マイグレーション実装
プラグイン有効化時に自動的にカラムを拡張するメソッドを追加します。
実装コード:
private function fix_token_column_length() {
global $wpdb;
// 現在のカラム定義を確認
$column_info = $wpdb->get_row(
"SHOW COLUMNS FROM {$this->table_name} WHERE Field = 'token'"
);
// varchar(64)の場合のみ拡張
if ($column_info && strpos($column_info->Type, 'varchar(64)') !== false) {
$result = $wpdb->query(
"ALTER TABLE {$this->table_name} MODIFY token varchar(512) NOT NULL"
);
if ($result !== false) {
error_log("Extended token column length in {$this->table_name}");
}
}
}
ステップ3:コンストラクタでの呼び出し
public function __construct() {
// ...
$this->maybe_create_table();
$this->fix_duplicate_indexes();
$this->fix_token_column_length(); // 追加
}
学んだこと
-
JWTトークンの長さを事前に計算する
JWT形式のトークンを保存する場合、ペイロードの内容によって長さが変動します。
十分な余裕を持ったカラムサイズを設定しておくことが重要です。
-
自動マイグレーションパターン
WordPressプラグインでは dbDelta() だけでなく、既存テーブルのカラム変更も考慮した自動マイグレーション機能が有効です。
-
エラーメッセージの読み解き
「値が長すぎる」というDBエラーは、カラムサイズと実際のデータ長の不一致を示唆しています。
-
テスト環境との差異
テスト環境ではマイグレーションがスキップされる設計になっていたため、本番で初めて問題が顕在化しました。
動作確認の手順
- ローカル環境でプラグイン有効化後、テーブル構造確認:
DESCRIBE wp_permit_tokens
token カラムが varchar(512) になっていることを確認
- JWT Permit Token生成テスト: トークン長が181バイト程度で正常に保存されることを確認
- 実際のサービスから画像生成テストを実行し、エラーが解消されていることを確認
改善結果
この修正により、以下の効果が得られました。
- エラー発生率: 100% → 0%
- 対応可能トークン長: 64バイト → 512バイト(約8倍の余裕)
- 既存環境への影響: 自動マイグレーションにより追加作業不要
特に自動マイグレーションパターンは、今後のスキーマ変更でも活用できそうです。
よくある質問(FAQ)
Q. JWTトークンの保存にはvarcharとtextどちらが適切?
A. varchar(512)を推奨します。
JWTトークンの長さは通常200〜300バイト程度で収まります。
varchar(512)なら十分な余裕があり、インデックスも作成可能です。
text型はインデックス作成に制限があるため、トークンの一意性チェックが必要な場合は避けた方が良いでしょう。
Q. なぜvarchar(255)ではなくvarchar(512)にしたのか?
A. ペイロードの拡張に備えるためです。
JWTのペイロードにはユーザー情報やクレームを追加できます。
将来的にペイロードが増える可能性を考慮し、余裕を持ってvarchar(512)としました。
ストレージへの影響は微小なので、安全側に倒しています。
Q. 既存データへの影響はある?
A. ありません。
ALTER TABLE ... MODIFYでカラムサイズを拡張する場合、既存データはそのまま保持されます。
縮小する場合はデータ切り詰めのリスクがありますが、拡張であれば安全です。
Q. dbDelta()でカラムサイズ変更はできないのか?
A. dbDelta()はカラムサイズ変更に対応していません。
WordPressのdbDelta()関数は、新規カラム追加やインデックス作成には対応していますが、既存カラムの型変更には対応していません。
そのため、今回のような自動マイグレーション処理を別途実装する必要があります。
まとめ
JWTトークンをデータベースに保存する際は、トークンの実際の長さを事前に計算することが重要です。
- JWTトークンは最低でも180バイト以上になる
varchar(64)では確実に不足する
- **varchar(512)**程度を設定しておけば安心
- 既存テーブルには自動マイグレーションで対応可能
この問題は地味ですが、本番環境で突然発生すると焦ります。
事前に適切なカラムサイズを設計しておきましょう。