Help us understand the problem. What is going on with this article?

curlで送ったPOSTリクエストがリダイレクトされる時-XPOSTを使うか否かで挙動が変わる

結論

$ curl -X POST -d "" -L <url>

でPOSTリクエストを送ると301/302/303リダイレクト後もPOSTが適用される。
一方

$ curl -d "" -L <url>

とPOSTリクエストを送るとリダイレクト時にGETに切り替わる。

補足

-Xはリクエストメソッドを指定するオプションです。

-dはPOSTリクエストで送るデータを指定するオプションです。
このコマンドを利用すると-Xで指定せずともPOST扱いになります。

-Lはリダイレクトを許可するオプションです。

実行環境

  • macOS 10.14.6
  • curl 7.54.0

マニュアルの記述

$ man curl

の内容にしっかり記述がありました。

-L, --location

    (前略)

    When curl follows a redirect and the request is not a plain GET (for example POST or PUT), it will do the following request with
    a  GET  if  the HTTP response was 301, 302, or 303. If the response code was any other 3xx code, curl will re-send the following
    request using the same unmodified method.

    You can tell curl to not change the non-GET request method to GET after a 30x response by using the dedicated options for  that:
    --post301, --post302 and --post303.

基本的に-LオプションはPOSTを301/302/303リダイレクトする時にGETに切り替えるみたいです。
そしてそれを拒否するためのオプションも用意されています。

-X, --request <command>

    (前略)

    The  method  string  you  set  with -X, --request will be used for all requests, which if you for example use -L, --location may
    cause unintended side-effects when curl doesn't change request method according to the HTTP 30x response codes - and similar.

    (後略)

-Xオプションで指定したメソッドは全てのリクエストに適用されるから-Lオプションの上記の仕様を上書きするみたいです。

検証

DockerでNginxサーバー建ててリダイレクトを設定し、curlコマンドを使って動作を検証しました。

設定

$ tree
.
├── docker-compose.yml
├── html
│   └── after.html
└── nginx.conf
docker-compose.yml
version: '3'

services:
  nginx:
    image: nginx:1.17.3
    ports:
      - 8080:80
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
      - ./html:/var/www/html
nginx.conf
server {
  listen 80;
  root /var/www/html;
  rewrite ^/before.html$ after.html redirect;
}
after.html
Redirected.

curlでbefore.htmlを取得するとafter.htmlに302リダイレクトされます。

$ curl -L http://localhost:8080/before.html
Redirected.

挙動の確認

-vオプションをつけるとcurlの実行結果の詳細を見ることが出来ます。
このオプションを使って2つの送信方法の結果のdiffを見ようと思います。

$ curl -v -X POST -d "" -L http://localhost:8080/before.html 2> result_x_on
$ curl -v -d "" -L http://localhost:8080/before.html 2> result_x_off
$ diff result_x_on result_x_off

diffの結果からはタイムスタンプ等本件と関係ない結果を省いています。

1d0
< Note: Unnecessary use of -X or --request, POST is already inferred.
28c27
< > POST /after.html HTTP/1.1
---
> > GET /after.html HTTP/1.1
33c32
< < HTTP/1.1 405 Not Allowed
---
> < HTTP/1.1 200 OK

このように、-X POSTを指定したものは302リダイレクト後もPOSTリクエストをしており、405を返されています。一方-X POSTを省略したものは302リダイレクト後はGETリクエストをしており、200を返されています。

-X POSTを指定したものは1行目で「POSTって事は分かってるから-X必要ないよ」と言われていますが、このように挙動が変わるため注意が必要です。

考察

POSTを301/302/303リダイレクトする時の挙動

多くのブラウザではPOSTを301/302/303リダイレクトする時GETに切り替えるようです。

GET メソッドは変更しません。
他のメソッドは GET に変更されるかもしれません。

HTTP のリダイレクト - HTTP | MDN (https://developer.mozilla.org/ja/docs/Web/HTTP/Redirections)

Mozillaのドキュメントでは上記の記述があり、わりと曖昧なようです。

Note: For historical reasons, a user agent MAY change the request
method from POST to GET for the subsequent request. If this
behavior is undesired, the 307 (Temporary Redirect) status code
can be used instead.

RFC 7231 - Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content (https://tools.ietf.org/html/rfc7231)

RFCでは上記のように注釈されています。
「歴史的な理由でこういう挙動になってるから嫌だったら307を返してね」
とのことです。

-Xオプションの使い方

上記のブラウザの仕様を見る限り、おそらく-Xオプションの「リダイレクト時もメソッドを維持する」という仕様を求める状況の方が特殊なのではないでしょうか?
今まで思考停止で
「POSTだったら-XPOSTに-dでデータ付けて〜😊」
とやっていましたが基本的に-dだけにした方が安全じゃないですかね?

実際に問題になったシチュエーション

GoogleAppsScript(GAS)のdoPost関数のテストをcurlで行おうとしたらリダイレクトを求められ、-Xオプションと-Lオプションを併用した上に上記の仕様を理解しておらずハマりました。
ご注意ください。

GASのdoPost関数をcurlでテストする時リダイレクトが必要なら-Xオプションを使わない

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away