nginxに振り回される

Posted by nogajun - 2022/02/11

古いURLでアクセスした人のためにnginxのリダイレクト設定を書いてたけど、もー、わからん!

ここの旧URLでは www.nofuture.tv/diary/ に日記とRSSがありました。これは楽勝で設定。

    rewrite "^/diary/index\.rdf$"  /rss.xml? permanent;
    rewrite "^/diary/?$"  /? permanent;

書き換え後の末尾のクエスチョンマークはクエリを付けない設定です。 次に日記の日付ごとのページのURLパターンは、こんな感じで3パターンあります。

  1. /diary/20220108.html#p01
  2. /diary/?date=20220108#p01
  3. /diary/index.rb?date=20220108#p01

これを www.nofuture.tv/20220108 の形に書き換えます。

URLパターンは元々、3ですがApacheのDirectoryIndexにindex.rbを設定しておくと2で来るパターンもあります。1はWebサーバーのrewriteで書き換えてhtmlファイルに見せかけているパターンで基本的にはこれを使っていました。3もあっさり設定完了。

     rewrite "^/diary/(\d{8})\.html" /$1? permanent;

日付の正規表現は以前の設定も雑だったので同じような感じで書き換えます。 問題は1と2。クエリなのでrewriteで書き換えられません。どうしたものかと悩んで調べたら、nginxの$argsと$arg_nameに値が入っているとのことなので、これを使えばあっさり解決…しない。

ifでどうにかすればと弄り倒して数時間。結局、この形に落ち着きました。アンカーリンクも取り出すように設定したけど、nginxでは取れないのでこの設定は意味がないです。そして、ここのifは次のcategoryで試行錯誤したあとに書き直した結果の形なので最初はもっとゴテゴテしてました。

    if ( $args ~ "date=(\d{8})(#(p|c)\d{2})?" ){
        return 301 /$1;
    }

これでやっつけたと思ったらカテゴリーごとのページがあったことを思い出しました。カテゴリーごとのページのURLパターンはこんな感じです。

  1. /diary/?category=hoge
  2. /diary/?category=hoge;year=2021
  3. /diary/?year=2021;category=hoge

カテゴリーはタグとして扱っていたので www.nofuture.tv/tag/hoge に飛ばすのが妥当かと考え、 $arg_category を使おうとしたらクエリの区切り文字にセミコロンを使っているので2のパターンではyearも一緒に出てきます。使えねー!それはさておき、どうするか考えて止まること数時間。結局、 $args そのままを正規表現で判定して使うことにして、こんなふうに落ち着きました。

if ( $args ~ "category=(\w+)(;?.*)" ){
  return 301 /tag/$1;
}

ほかにはスラッシュのあり・なしでの挙動にハマったりしたけれど上に書いたものをまとめるとこんな形になりました。長かった…。

location /diary/ {
    if ( $args ~ "category=(\w+)(;?.*)" ){
        return 301 /tag/$1;
    }
    if ( $args ~ "date=(\d{8})(#(p|c)\d{2})?" ){
        return 301 /$1;
    }
    rewrite "^/diary/(\d{8})\.html" /$1? permanent;
    rewrite "^/diary/index\.rdf$"  /rss.xml? permanent;
    rewrite "^/diary/?$"  /? permanent;
}

rewriteの設定をするときは、nginxの設定ファイルに以下の設定をすると、ログにどこにマッチしたか流れるのでおすすめです。あと、この設定でエラーログを/dev/stderrに流しているのはDockerだからです。リアルにnginxを使っている人は適当なファイルに書き出してください。

    rewrite_log on;
    error_log /dev/stderr notice;

リダイレクトの設定ができたので、やっとブログが書けると思い、ブログのカバー画像をアップロードしたら画像のアップロードしている最中に止まります。ログを確認するとこんなメッセージが出ていました。

    [error] 22#22: *6 client intended to send too large body

なんだろうと思って検索すると、nginxのPOSTの最大サイズが1Mしかなく結構みなさん引っかかってる設定だそう。そうと分かれば、client_max_body_sizeを多めに設定。

    client_max_body_size  100m;

これで本当にやっとブログが書けると書いているのがこのエントリーでした。長かった…。お疲れ様でした。