tkuchikiの日記

Linux やプログラミングについて書きます。

Nginx で 404 をキャッシュさせない

nginx で 404をキャッシュさせないようにする場合、

add_header Cache-Control "no-cache, no-store";
return 404 "not found";

と設定しても、Cache-Control ヘッダが付与されません。

Module ngx_http_headers_module を見ると、

Adds the specified field to a response header provided that the response code equals 200, 201, 204, 206, 301, 302, 303, 304, or 307. A value can contain variables.

とあるので、add_header で 404 response に Cache-Control ヘッダを追加できません。

404 で Cache-Control ヘッダを追加する場合は、HttpHeadersMoreModule - Nginx Community を使えば可能になります。
HttpHeadersMoreModule はデフォルトでは入っていないので、ビルド時に add-module で追加する必要があります。

以下、設定ファイルと curl の結果です。

設定ファイル
server {
    listen 80;
    server_name localhost;

    location  =/404 {
        default_type application/json;
        try_files $uri =404;
        error_page 404 = @notfound;
    }

    location @notfound {
        more_set_headers -s 404 "Cache-Control: no-cache, no-store";
        return 404 " ";
    }
}
404
$ curl "http://localhost/404"
 
$ curl -I "http://localhost/404"
HTTP/1.1 404 Not Found
Server: ngx_openresty/1.4.3.6
Date: Fri, 10 Oct 2014 07:25:32 GMT
Content-Type: application/octet-stream
Content-Length: 1
Connection: keep-alive
Cache-Control: no-cache, no-store

return 404 で半角スペース1文字を返しているのは、
空文字だとデフォルトの 404 ページを返すようになっているからです。

おそらく https://github.com/nginx/nginx/blob/master/src%2Fhttp%2Fngx_http_script.c#L1355-1373 が該当のコードだと思うのですが、
code->text の長さを見ているので、空文字だと上書きされないのだと思います。

もし、空の response を返したい場合は、空の html を用意してそれを返すようにするか、
HttpEchoModule - Nginx Community を使って、

echo -n "";

とすれば、空の response を返せます。

以下が、実験時の設定ファイルと結果です。

設定ファイル
server {
    listen 80;
    server_name localhost;


    location =/echo {
        try_files $uri =404;
        error_page 404 =404 @echo;
    }

    location @echo {
        more_set_headers -s 404 "Cache-Control: no-cache, no-store";
        echo -n "";
    }

    location =/empty.html {
        try_files $uri =404;
        error_page 404 =404 @empty;
    }

    location @empty {
        more_set_headers -s 404 "Cache-Control: no-cache, no-store";
        root /var/www/html;
        index empty.html;
    }
}
echo 結果
$ curl "http://localhost/echo"
$ curl -I "http://localhost/echo"
HTTP/1.1 404 Not Found
Server: ngx_openresty/1.4.3.6
Date: Fri, 10 Oct 2014 10:33:10 GMT
Content-Type: application/octet-stream
Connection: keep-alive
Cache-Control: no-cache, no-store
empty.html 結果
$ curl "http://localhost/empty.html"
$ curl -I "http://localhost/empty.html"
HTTP/1.1 404 Not Found
Server: ngx_openresty/1.4.3.6
Date: Fri, 10 Oct 2014 10:34:27 GMT
Content-Type: text/html
Content-Length: 0
Connection: keep-alive
ETag: "5437b19d-0"
Cache-Control: no-cache, no-store

一見同じですが、echo の方は、Content-Length ヘッダがついておらず、
$body_bytes_sent が 5 となっていました。
default_type application/octet-stream; としていると、
content-type がないので octet-stream になりますが、それが原因なようでした。
content-type を text/html になるように設定すれば、echo の方でも $body_bytes_sent が 0 になりました(Content-Length はつかないままでした)。

以上のことから、404 で空の response を返す場合は、

  • 空の html を返す
  • content-type を text/html(application/octet-stream 以外?)にして echo -n "" を使う

とする必要があります。