特定のHTTPステータスコードを発生させる

HTTPステータスコードってありますよね。
普通にインターネットしてるだけでも「404 Not Found」、「500 Internal Server Error」あたりはけっこうな頻度で目にします。
障害調査なんかで、その時は発生していたけど後から気づいたときに治っていたりして、どうやって再現させようと困ることが多いので、今回は「このコードなんだっけ」というときに意図的に特定のステータスコードを再現させてみたいと思います。

※4xx系と5xx系のエラーコードをメインに試します。

詳しい解説などはWikipediaでどうぞ…

HTTPステータスコード – Wikipedia

環境を準備する

今回はAWSでELBとEC2を準備し、EC2(ubuntu)にNginxとApache、使うかわかりませんがphpを入れたいと思います。
既に手持ちのサーバーなどがある場合は読み飛ばしてください。

EC2作成

EC2をローンチしておきます。

@local PC

ssh接続

$ ssh -i  ubuntu@<サーバーのIP>

@EC2

各種インストール

$ sudo apt-get update
$ sudo apt-get -y upgrade
$ sudo apt-get -y install nginx-extras
$ sudo apt-get -y install apache2
$ sudo apt-get -y install php7.0
$ sudo apt-get -y install libapache2-mod-php

Apache設定

$ sudo vi /etc/apache2/apache2.conf
 
 	Options Indexes FollowSymLinks
-	AllowOverride None
+	AllowOverride All
 	Require all granted
 

$ sudo vi /etc/apache2/ports.conf
-Listen 80
+Listen 8080

$ sudo vi /etc/apache2/sites-available/000-default.conf
-
+

$ sudo vi /etc/apache2/mods-available/dir.conf
-	DirectoryIndex index.html index.cgi index.pl index.php index.xhtml index.htm
+	DirectoryIndex index.php

$ sudo service apache2 start

起動確認

$ sudo netstat -anp | egrep "nginx|apache2" | grep ^tcp
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      13441/nginx -g daem
tcp6       0      0 :::8080                 :::*                    LISTEN      14800/apache2   
tcp6       0      0 :::80                   :::*                    LISTEN      13441/nginx -g daem

phpinfo作成

$ sudo vi /var/www/html/index.php

Nginx設定

$ sudo vi /etc/nginx/sites-available/default
 	location / {
-		try_files $uri $uri/ =404;
+		proxy_pass http://localhost:8080/;
 	}

http://EC2のIP or DNS名/ で phpinfoページが表示されればOKです。

ここまできたらELBも作成し、80->80のリスナーを作成しておきます。

やってみる

さっそく試してみます。
再現が難しかったり、手間がかかりそうなステータスコードは省略します。。
curlやtelnetを実行するのはクライアントとなるPC上です。コードごとに入れた設定は次のコードの時にはクリアした状態です。

400 Bad Request

リクエストが不正である。定義されていないメソッドを使うなど、クライアントのリクエストがおかしい場合に返される。

おかしいメソッドでリクエストしてみます。

$ curl http://hoge.elb.amazonaws.com/ -X 1234 -I
HTTP/1.1 400 Bad Request
Content-Type: text/html
Date: Xxx, 01 Jan 2017 00:00:00 GMT
Server: nginx/1.10.0 (Ubuntu)
Content-Length: 182
Connection: keep-alive

400が返ってきました。

401 Unauthorized

認証が必要である。

Basic認証を設定します。

$ sudo htpasswd -c -b /etc/apache2/.htpasswd user password
Adding password for user user

$ sudo vi /var/www/html/.htaccess
AuthUserFile /etc/apache2/.htpasswd
AuthName "Basic Auth"
AuthType Basic
Require valid-user

リクエストしてみます。

$ curl http://hoge.elb.amazonaws.com/ -I
HTTP/1.1 401 Unauthorized
Content-Type: text/html; charset=iso-8859-1
Date: Xxx, 01 Jan 2017 00:00:00 GMT
Server: nginx/1.10.0 (Ubuntu)
WWW-Authenticate: Basic realm="Basic Auth"
Connection: keep-alive

401が返ってきました。

403 Forbidden

禁止されている。リソースにアクセスすることを拒否された。

すべてdenyします。

$ sudo vi /var/www/html/.htaccess
require all denied

リクエストしてみます。

$ curl http://hoge.elb.amazonaws.com/ -I
HTTP/1.1 403 Forbidden
Content-Type: text/html; charset=iso-8859-1
Date: Xxx, 01 Jan 2017 00:00:00 GMT
Server: nginx/1.10.0 (Ubuntu)
Connection: keep-alive

403が返ってきました。

404 Not Found

未検出。リソースが見つからなかった。

存在しないURLにリクエストしてみます。

$ curl http://hoge.elb.amazonaws.com/404 -I
HTTP/1.1 404 Not Found
Content-Type: text/html; charset=iso-8859-1
Date: Xxx, 01 Jan 2017 00:00:00 GMT
Server: nginx/1.10.0 (Ubuntu)
Connection: keep-alive

404が返ってきました。

405 Method Not Allowed

許可されていないメソッド。

許可されていなさそうなメソッドでリクエストしてみます。

$ curl http://hoge.elb.amazonaws.com/ -X TRACE -I
HTTP/1.1 405 Not Allowed
Content-Type: text/html
Date: Xxx, 01 Jan 2017 00:00:00 GMT
Server: nginx/1.10.0 (Ubuntu)
Content-Length: 182
Connection: keep-alive

405が返ってきました。

406 Not Acceptable

受理できない。Accept関連のヘッダに受理できない内容が含まれている場合に返される。

これはちょっと手間がかかる予感。
サーバー側の判定次第なような気もするので、406を返すようにApacheを設定します。

$ sudo apt-get install libapache2-mod-security2
$ sudo cp -p /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
$ sudo vi /etc/modsecurity/modsecurity.conf
+ SecRule REQUEST_URI "406" "id:'999999',log,deny,status:406"
$ service apache2 restart

設定したURIにリクエストしてみます。

$ curl http://hoge.elb.amazonaws.com/406 -I
HTTP/1.1 406 Not Acceptable
Content-Type: text/html; charset=iso-8859-1
Date: Xxx, 01 Jan 2017 00:00:00 GMT
Server: nginx/1.10.0 (Ubuntu)
Connection: keep-alive

406が返ってきました。

408 Request Timeout

リクエストタイムアウト。リクエストが時間以内に完了していない場合に返される。

curlだと厳しそうなので、telnetで接続して放置してみます。

$ telnet hoge.elb.amazonaws.com 80
Trying xxx.xxx.xxx.xxx...
Connected to hoge.elb.amazonaws.com.
Escape character is '^]'.
GET / HTTP/1.1
<-ここで放置
HTTP/1.1 408 REQUEST_TIMEOUT
Content-Length:0
Connection: Close

408が返ってきました。

410 Gone

消滅した。リソースは恒久的に移動・消滅した。どこに行ったかもわからない。

これは404で返すサーバーのほうが多そうです。。
mod_rewriteで無理やり返してみます。

$ sudo ln -s /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled/
$ sudo service apache2 reload

$ sudo vi /var/www/html/.htaccess
RewriteEngine On
RewriteRule ^index\.php$ - [G]

設定したコンテンツにリクエストしてみます。

$ curl http://hoge.elb.amazonaws.com/index.php -I
HTTP/1.1 410 Gone
Content-Type: text/html; charset=iso-8859-1
Date: Xxx, 01 Jan 2017 00:00:00 GMT
Server: nginx/1.10.0 (Ubuntu)
Connection: keep-alive

410が返ってきました。

413 Payload Too Large

ペイロードが大きすぎる。リクエストエンティティがサーバの許容範囲を超えている場合に返す。

大きなContent-Lengthヘッダを突っ込んでリクエストしてみました。

$ curl http://hoge.elb.amazonaws.com/ -H "Content-Length: 10000000" -I
HTTP/1.1 413 Request Entity Too Large
Content-Length: 208
Content-Type: text/html
Date: Xxx, 01 Jan 2017 00:00:00 GMT
Server: nginx/1.10.0 (Ubuntu)
Connection: Close

413が返ってきました。

414 URI Too Long

URIが大きすぎる。URIが長過ぎるのでサーバが処理を拒否した場合に返す。

8192バイト以上で発生するというような情報を見かけたのでそれ以上の長いURIでリクエストしてみました。

$ curl http://hoge.elb.amazonaws.com/?parameter=(略) -I
HTTP/1.1 414 Request-URI Too Large
Content-Length: 202
Content-Type: text/html
Date: Xxx, 01 Jan 2017 00:00:00 GMT
Server: nginx/1.10.0 (Ubuntu)
Connection: keep-alive

414が返ってきました。

417 Expectation Failed

レンジは範囲外にある。実リソースのサイズを超えるデータを要求した。具体例として、Expect:ヘッダに100-continue以外の変なものを入れた場合や、そもそもサーバが100 Continueを扱えない場合に返す。

具体例に従ってリクエストしてみます。

$ curl http://hoge.elb.amazonaws.com/ -H "Expect: 001-continue" -X POST -v
*   Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to hoge.elb.amazonaws.com (xxx.xxx.xxx.xxx) port 80 (#0)
> POST / HTTP/1.1
> Host: hoge.elb.amazonaws.com
> User-Agent: curl/7.51.0
> Accept: */*
> Expect: 001-continue
> 
< HTTP/1.1 417 EXPECTATION_FAILED
< Content-Length: 0
< Connection: Close
< 
* Curl_http_done: called premature == 0
* Closing connection 0

417が返ってきました。

500 Internal Server Error

サーバ内部エラー。サーバ内部にエラーが発生した場合に返される。

誤った設定を入れました。

$ sudo vi /var/www/html/.htaccess
適当な文字列(例えば「500」とか)

リクエストしてみます。

$ curl http://hoge.elb.amazonaws.com/ -I
HTTP/1.1 500 Internal Server Error
Content-Type: text/html; charset=iso-8859-1
Date: Xxx, 01 Jan 2017 00:00:00 GMT
Server: nginx/1.10.0 (Ubuntu)
Connection: keep-alive

500が返ってきました。

502 Bad Gateway

不正なゲートウェイ。ゲートウェイ・プロキシサーバは不正な要求を受け取り、これを拒否した。

Apacheを止めます。

$ sudo service apache2 stop

リクエストしてみます。

$ curl http://hoge.elb.amazonaws.com/ -I
HTTP/1.1 502 Bad Gateway
Content-Length: 182
Content-Type: text/html
Date: Xxx, 01 Jan 2017 00:00:00 GMT
Server: nginx/1.10.0 (Ubuntu)
Connection: keep-alive

502が返ってきました。
※Apacheの領域にELB用のヘルスチェックコンテンツを置いている場合は、502の後、OutOfServiceで503になります。

503 Service Unavailable

サービス利用不可。サービスが一時的に過負荷やメンテナンスで使用不可能である。

ELBのヘルスチェックコンテンツをリネームしてヘルスチェックに失敗させます。

$ sudo mv /var/www/html/index.html /var/www/html/index.html_

EC2のインスタンスステータスがOutOfServiceになったらリクエストしてみます。

$ curl http://hoge.elb.amazonaws.com/ -I
HTTP/1.1 503 Service Unavailable: Back-end server is at capacity
Connection: keep-alive

503が返ってきました。

504 Gateway Timeout

ゲートウェイタイムアウト。ゲートウェイ・プロキシサーバはURIから推測されるサーバからの適切なレスポンスがなくタイムアウトした。

これは例えばバックエンドのアプリケーションで処理が止まってレスポンスが返ってこない状態で、ELBのアイドルタイムアウトに達したときなどに起こりえます。
サーバー側の環境にもよりますが、今回は以下の内容でリクエストしてみます。

$ curl http://hoge.elb.amazonaws.com/ -H "Content-Length: 01" -I
HTTP/1.1 504 GATEWAY_TIMEOUT
Connection: keep-alive

504が返ってきました。

まとめ

たったこれだけ再現させるのも探り探りだったのでまあまあメンドくさかったです。
ステータスコードの解説はあっても、意外に実際どうやったら起きるのかは少なかったので書いてみました。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です