2026年5月30日
2026年5月30日
Nginxのmapとハッシュ設定を最適化する方法|map_hash_max_size・map_hash_bucket_sizeと「could not build map_hash」の直し方
Nginxで could not build map_hash, you should increase map_hash_bucket_size が出たら、mapのキーの中に長すぎるものがあるので map_hash_bucket_size を128などに上げます。could not build optimal map_hash の警告であれば、キーの数に対して map_hash_max_size が小さいので、まずこちらを上げます。この記事では、2つのメッセージの違いと値の決め方、そして大量のリダイレクトなどmapを大きく使うときに速さを保つ書き方を説明します。
mapディレクティブの基本
map は、ある値(ソース)をもとに新しい変数の値を決める仕組みです。if を並べるよりも見通しがよく、評価も効率的です。
http {
map $http_user_agent $is_bot {
default 0;
"~*googlebot" 1;
"~*bingbot" 1;
}
map $host $site_env {
default production;
staging.example.com staging;
dev.example.com development;
}
}
押さえておきたい性質は次のとおりです。
mapはhttpコンテキストにだけ書けます(TCP/UDPのstreamでは同名の別モジュールがあります)。serverやlocationの中に書くとエラーになります- 値は、その変数が実際に使われたときに初めて計算されます。mapをいくつ定義しても、使われない変数のぶんの処理は発生しません
~で始まるキーは大文字小文字を区別する正規表現、~*は区別しない正規表現ですdefault、hostnames、include、volatileは特別な意味を持つパラメータです。文字どおり「default」というキーを使いたい場合は、先頭にバックスラッシュを付けて\defaultと書きます
mapがキーを探す順番とハッシュテーブル
Nginxはmapの文字列キーを、起動時(設定の読み込み時)にハッシュテーブルへ格納します。リクエスト時はハッシュで一発で引けるため、キーが1万件あっても検索の速さはほとんど変わりません。
一方、正規表現のキーはハッシュに入りません。文字列キーに一致しなかった場合に、設定に書いた順に1つずつ照合されます。照合の優先順位は次のとおりです。
- マスクなしの文字列キー(完全一致)
hostnamesを指定したときの、前方マスク付きの最長一致(*.example.comなど)- 同じく後方マスク付きの最長一致(
www.example.*など) - 最初に一致した正規表現
defaultの値
つまり、ハッシュのサイズ設定(map_hash_*)が影響するのは1〜3の文字列キーだけです。mapが遅いと感じる原因の多くは、ハッシュではなく大量の正規表現キーにあります。
2つのメッセージの意味
Nginxがハッシュを作れなかったときに出すメッセージは2種類あり、意味も深刻度も違います(文面は2026年10月現在の安定版のものです)。
nginx: [emerg] could not build map_hash, you should increase map_hash_bucket_size: 64
nginx: [warn] could not build optimal map_hash, you should increase either map_hash_max_size: 2048 or map_hash_bucket_size: 64; ignoring map_hash_bucket_size
1つ目(emerg) は、1つのキーがバケット(ハッシュの1区画)に収まらないほど長いことを示しています。致命的なエラーなので nginx -t が失敗し、設定の再読み込みもできません。原因は「キーの数」ではなく「最長のキーの長さ」なので、map_hash_max_size をいくら増やしても解消しません。
2つ目(warn) は、指定された最大サイズの範囲内では、各バケットを map_hash_bucket_size に収める配置が見つからなかったことを示しています。Nginxはバケットサイズの指定を無視してハッシュを作るので動作は続きますが、テーブルが最適な形になっていません。古いバージョンのNginxではこの状況でも起動に失敗していたため、古い環境では似た文面のemergとして出ることがあります。
map_hash_bucket_size の決め方
既定値はCPUのキャッシュラインサイズに依存し、32・64・128のいずれかです。多くの64ビット環境では64になります。キャッシュラインは次のコマンドで確認できます。
getconf LEVEL1_DCACHE_LINESIZE
1つのキーに必要な領域は、キーのバイト長にポインタ分の管理領域を足し、8バイト単位に切り上げたものです。64ビット環境での目安として、map_hash_bucket_size 64 ではおよそ46バイト、128 ではおよそ110バイト、256 ではおよそ238バイトまでのキーが収まります。ドメイン名を並べるmapなら64で足りることが多く、URLのパスをキーにするリダイレクト用のmapでは128や256が必要になりがちです。
最長のキーの長さは、mapのファイルからバイト数で確認します。LC_ALL=C を付けないと、日本語を含むキーが文字数で数えられて短く見積もられます。
LC_ALL=C awk '{ print length($1) }' /etc/nginx/redirects.map | sort -n | tail -n 1
設定値はキャッシュラインサイズの倍数に切り上げて使われるので、64・128・256のような2のべき乗で指定します。
map_hash_max_size の決め方
既定値は2048です。キーの数がこれに近づくか超えると、2つ目の警告が出やすくなります。Nginxの公式ドキュメントでは、max_sizeとbucket_sizeのどちらかを増やすよう求められた場合は、まずmax_sizeを増やすよう案内されています。目安として、キーの数より大きい2のべき乗を指定します。
http {
map_hash_max_size 16384;
map_hash_bucket_size 128;
...
}
この2つは http コンテキストに書き、そのサーバー内のすべてのmapに共通で適用されます。ハッシュは設定の読み込み時に一度だけ作られるため、値を大きめにしても増えるのはわずかなメモリだけで、リクエストごとの処理が重くなることはありません。
キーの数は次のように数えます。コメント行や空行があれば除いて数えてください。
grep -cvE '^\s*(#|$)' /etc/nginx/redirects.map
大量リダイレクトをmapで書く例
サイトリニューアルで数千件の旧URLを新URLへ転送する場合、location や rewrite を数千行並べるより、mapにまとめたほうが速く、管理もしやすくなります。
http {
map_hash_max_size 16384;
map_hash_bucket_size 128;
map $uri $redirect_to {
default "";
include /etc/nginx/redirects.map;
}
server {
listen 443 ssl;
server_name example.com;
if ($redirect_to) {
return 301 $redirect_to;
}
...
}
}
# /etc/nginx/redirects.map
/old-page/ /new-page/;
/blog/2019/hello/ /articles/hello/;
/campaign/ https://campaign.example.com/;
ここでのつまずきどころは、ソースに使う変数の選び方です。
$uriは正規化済みのパスで、クエリ文字列を含まず、%E3%81%82のようなパーセントエンコードはデコードされ、連続したスラッシュはまとめられます。日本語URLをキーにするときは、デコード後の文字列で書きます$request_uriはブラウザから届いたままのURIで、クエリ文字列を含みます。?utm_source=...が付いたアクセスは一致しなくなるため、クエリまで含めて区別したい場合にだけ使いますif ($redirect_to)の中でreturnだけを使う書き方は、ifの中で安全に使える数少ないパターンの1つです
mapファイルを更新したら、nginx -t で構文とハッシュを確認してから systemctl reload nginx で反映します。
正規表現キーを速く保つ
ハッシュ設定では解決しない遅さへの対処です。
- 完全一致で書けるものは正規表現にしない。
"~^/about/$"は/about/と書けばハッシュで引けます - 正規表現が残る場合は、一致しやすいものを上に並べる。上から順に照合され、最初に一致した時点で止まるためです
pcre_jit on;をメインコンテキストに書くと、PCREのJITコンパイルが有効になり正規表現の照合が速くなります。NginxがJIT対応のPCREでビルドされている必要があり、nginx -Vの出力で確認できます
同じ仕組みを持つほかのハッシュ設定
mapと同じ形式のエラーは、ほかのハッシュでも出ます。名前が違うだけで、考え方は同じです。
server_names_hash_bucket_size/server_names_hash_max_size:server_nameに長いドメイン名や多数のドメインを書いたときtypes_hash_max_size/types_hash_bucket_size:mime.typesに多くの種類を登録したとき。Debian系の標準設定ではtypes_hash_max_size 2048;が書かれていますproxy_headers_hash_max_size/proxy_headers_hash_bucket_size:proxy_set_headerを多数定義したときvariables_hash_max_size/variables_hash_bucket_size:変数の数が多いとき
エラーメッセージの「could not build」の後ろの名前を見れば、どのディレクティブを調整すればよいかがわかります。最終的にどの値が効いているかは、nginx -T | grep -E '_hash_(max|bucket)_size' で読み込まれた設定全体から確認できます。
まとめ
[emerg] could not build map_hash ... map_hash_bucket_sizeは最長キーが長すぎる。map_hash_bucket_sizeを128や256に上げる[warn] could not build optimal map_hashはキー数に対してテーブルが小さい。まずmap_hash_max_sizeをキー数より大きい2のべき乗に上げる- ハッシュ設定が効くのは文字列キーだけ。正規表現キーは上から順に照合されるので、完全一致に置き換え、順序を工夫し、
pcre_jitを使う - リダイレクトのmapでは
$uriと$request_uriの違いに注意し、更新後はnginx -tで確認してから再読み込みする server_names_hashやtypes_hashなど、同じ形式のエラーも同じ考え方で直せる