nginx でリダイレクトを設定しようとして、rewrite に redirect や permanent を付ければよいと考えて始めたものの、「307 はどう書くのか」と手が止まった方は少なくないでしょう。結論から言えば、307 を返すには return ディレクティブを使います。rewrite のフラグだけでは、307 は返せません。

この記事では、nginx の公式文書に書かれている仕様に沿って、307 を返す設定の基本から、複数のルールを整理する方法、失敗しやすい点、動作の確認方法までを解説します。確認日は 2026 年 10 月 9 日です。

基本

return 307 URL; と書きます。

rewrite の限界

フラグ redirect は 302、permanent は 301 を返します。

複数ルール

map で対応表にまとめると、管理しやすくなります。

確認

nginx -t で構文を検査し、curl -I で応答を見ます。

rewrite のフラグだけでは、307 は返せない

nginx で転送を書く方法は 2 つあります。rewrite と return です。この 2 つの違いを知らずに始めると、行き詰まります。

nginx の rewrite ディレクティブの文書によると、置換先の文字列が http://、https://、$scheme で始まる場合は処理が止まり、転送が返されます。そして、フラグの意味は次のとおりです。

  • redirect: 一時的な転送(302)を返します。
  • permanent: 恒久的な転送(301)を返します。

つまり、rewrite で選べる転送の番号は、302 と 301 の 2 種類だけです。307 や 308 を返す手段として、rewrite は使えません。これを知らずに rewrite ... redirect; と書くと、意図せず 302 が返ります。

もう一つ、見落としやすい点があります。nginx の古いバージョンでは、307 や 308 を転送として扱いませんでした。return ディレクティブの文書には、「307 は、バージョン 1.1.16 と 1.0.13 以降で転送として扱われる」「308 は、バージョン 1.13.0 以降で転送として扱われる」という趣旨の記載があります。かなり古い nginx を使っている場合は、事前にバージョンを確認する必要があります。

return ディレクティブの基本

return の構文は、公式文書では次のとおりです。

return code [text];
return code URL;
return URL;

使える場所は server、location、if の中です。そして、転送の URL を指定できるコードは、301、302、303、307、308 です。それ以外のコードでは、第 2 引数は応答の本文になります。

最も単純な 307

特定のパスを、別の URL へ一時的に転送する例です。

location = /old-page {
    return 307 https://example.com/new-page;
}

location = は完全一致を意味します。/old-page へのアクセスに対してだけ、307 と Location: https://example.com/new-page を返します。

同じサーバーの別のパスへ転送する

転送先が同じサーバーであれば、パスだけを書けます。

location = /old-page {
    return 307 /new-page;
}

公式文書によれば、転送先をこのサーバー内の URI として指定した場合、完全な URL は、リクエストのスキーム($scheme)と、server_name_in_redirect、port_in_redirect の設定にしたがって組み立てられます。server_name_in_redirect の既定値は off で、そのとき Host ヘッダーの名前が使われます。port_in_redirect の既定値は on で、絶対 URL にポート番号が含まれます。リバースプロキシの背後に置く場合は、ポート番号が意図せず付与されないよう、設定を確認してください。

引数が URL だけの場合は 302

return に URL だけを指定する書き方もあります。

return https://example.com/;

この書き方では、302 が返ります。公式文書にも「URL だけを指定すると一時的な転送(302)になる」という趣旨の記載があります。307 にしたいなら、必ず return 307 URL; とコードを明示してください。

307 が向いている場面と、nginx での書き分け

nginx の設定でも、番号の選び方は通常の判断と同じです。

目的 書き方
一時的な転送(メソッドを保つ) return 307 URL;
一時的な転送(メソッドは問わない) return 302 URL; または rewrite ... redirect;
恒久的な転送(メソッドを保つ) return 308 URL;
恒久的な転送(メソッドは問わない) return 301 URL; または rewrite ... permanent;
POST の後の結果表示 アプリケーション側で 303 を返す(nginx の設定で行うことは少ない)

パスとクエリ文字列を保つ書き方

実運用でもっとも多い失敗は、転送先を固定の URL にしてしまい、元のパスやクエリ文字列が失われることです。変数を使って、元の URI を引き継ぎます。

公式文書によれば、$request_uri は「引数を含む、元のリクエストの URI 全体」です。一方、$uri は、処理の途中で変わりうる正規化後の URI です。転送で元の形を保ちたい場合は、$request_uri が適しています。

server {
    listen 80;
    server_name old.example.com;

    # 元のパスとクエリを保ったまま、新しいドメインへ一時転送
    return 307 https://new.example.com$request_uri;
}

ホスト名を保つなら $host が使えます。公式文書では、$host はリクエスト行のホスト名、なければ Host ヘッダーのホスト名、それもなければ一致したサーバー名の順で決まります。

server {
    listen 80 default_server;
    return 307 https://$host$request_uri;
}

この例は、どのホスト名で届いた http の要求も、同じホスト名の https に一時転送します。確認が済んだら、恒久的な転送(301 か 308)に切り替えます。

複数のルールを map で整理する

転送が数件なら location を並べても管理できますが、数十件になると見通しが悪くなります。その場合は、map で対応表にまとめます。

map は、ある変数の値に応じて別の変数の値を決める仕組みで、http コンテキストに書きます。公式文書によれば、変数は使われたときにだけ評価されるため、map を多数定義しても、リクエストごとの処理負荷は増えません。default を省略した場合、一致しなかったときの値は空文字列です。

http {
    # 旧 URI → 新 URI の対応表
    map $request_uri $moved_to {
        default          "";
        /old-a           /new-a;
        /old-b           /new-b;
        ~^/legacy/(.*)$  /archive/$1;
    }

    server {
        listen 80;
        server_name example.com;

        # 対応表に一致したときだけ、307 を返す
        if ($moved_to) {
            return 307 $moved_to;
        }
    }
}

~ で始まる値は大文字小文字を区別する正規表現です。~* なら区別しません。正規表現のキャプチャ($1)は、結果の値に使えます。if ($moved_to) は、変数が空文字列または 0 のときに偽となるので、一致しなかった場合は転送されません。

対応表の左側に $request_uri を使うと、クエリ文字列を含んだ形で照合される点に注意してください。公式文書のとおり、$request_uri は引数を含む URI 全体なので、/old-a?ref=1 のようなアクセスは、/old-a という文字列とは一致しません。クエリを無視して照合したい場合は、map $uri $moved_to のように、引数を含まない $uri を使います。ただし $uri は、処理の途中で変化しうる正規化後の値であることも、公式文書に書かれています。クエリを転送先にも引き継ぎたいときは、return 307 $moved_to$is_args$args; のように、$is_args と $args を後ろに付けます。

対応表が長くなる場合は、include で別ファイルに分けられます。

map $request_uri $moved_to {
    default "";
    include /etc/nginx/redirect-map.conf;
}

設定でつまずきやすい点

無限ループに注意する

転送先が、同じ設定に再び一致すると、永久に転送が続きます。たとえば、location /blog/ { return 307 /blog/; } のような規則は、転送先自身に当てはまります。完全一致(location =)を使うか、条件を限定してください。詳しくはリダイレクトが繰り返されるときの原因と直し方を参照してください。

プロキシの背後では、スキームに注意する

CDN やロードバランサーの背後にいる場合、nginx が見るスキームは、利用者の通信方式と異なることがあります。return 307 https://$host$request_uri; を無条件に書くと、すでに https で来た通信も転送してしまい、ループの原因になります。プロキシが付ける X-Forwarded-Proto などの情報を使って、http のときだけ転送するように条件を付けます。

恒久的な転送は、慎重に

確認の段階では 307 を使い、結果に問題がなければ 301 や 308 に切り替える流れが安全です。恒久的な転送は、ブラウザーなどに保存されるため、誤って公開すると取り消しにくくなります。

設定の確認方法

設定を反映する前に、構文を検査します。

nginx -t

問題がなければ、設定を再読み込みします。

nginx -s reload

そのうえで、実際の応答を curl で確認します。

curl -I http://example.com/old-page

次のような出力が得られれば、設定は意図どおりです。

HTTP/1.1 307 Temporary Redirect
Location: https://example.com/new-page

HTTP/1.1 302 と表示された場合は、return の第 1 引数が抜けていないか、rewrite ... redirect; を使っていないかを確認してください。パスやクエリが期待どおりに引き継がれているかも、Location の値で確認できます。

nginx で 307 を返すための要点

  • 307 を返すには、return 307 URL; を使います。rewrite のフラグでは 302 か 301 しか返せません。
  • return に URL だけを渡すと 302 になります。コードは必ず明示します。
  • 元のパスとクエリを保つには、$request_uri を使います。
  • 転送が多いときは、map で対応表にまとめます。
  • 反映前に nginx -t、反映後に curl -I で確認します。
  • 確認が済んだら、必要に応じて恒久的な転送へ切り替えます。

同じ転送を Apache や PHP で書きたい場合は、Apache での設定、PHP での設定も参考にしてください。