Nginxで特定のContent-Typeならブロックする

注意事項

この記事は、Notion AIに対して

  • 問題と解決策の概要
  • 例示用のコード

を渡して生成した実験的な記事になります。

概要

特定のエンドポイントから返されるJSONファイルを識別する方法を探していました。 レスポンスヘッダーのContent-Typeを見てエラーを返すことで実現しました。OpenRestyとLuaモジュールを使うことで実現できます。

手順

  1. 検証用のdocker-compose.yamlを用意する
  2. ApplicationからのレスポンスヘッダーのContent-Typeがapplication/jsonだったら418を返すためのNginxの設定ファイルを用意する
  3. 意図した通りの結果が返ってくるかを確認するためのスクリプトを用意する

詳細

1. 検証用のdocker-compose.yamlを用意する

まず、検証用のdocker-compose.yamlを用意します。DockerHub公式のNginxイメージではレスポンスヘッダーのContent-Typeを見てエラーにすることができないため、Luaモジュールであれば、レスポンスヘッダーのContent-Typeを見てエラーにすることができるLuaが使えるNginxのコンテナイメージであるOpenRestyを使うことにします。結果が正常に返されるかを確認するために、upstreamからのレスポンスヘッダーがapplication/jsonだったら418を返すようにします。

services:
  frontend:
    image: openresty/openresty:alpine
    volumes:
    - ./proxy.conf:/etc/nginx/conf.d/default.conf
    ports:
    - 3000:80
    depends_on:
    - backend

  backend:
    image: nginx:alpine
    volumes:
    - ./backend.conf:/etc/nginx/conf.d/default.conf

2. ApplicationからのレスポンスヘッダーのContent-Typeがapplication/jsonだったら418を返すためのNginxの設定ファイルを用意する

次に、ApplicationからのレスポンスヘッダーのContent-Typeがapplication/jsonだったら418を返すためのNginxの設定ファイルを用意します。以下がproxy.confの例です。

init_by_lua_block {
  CONTENT_TYPE_BLACKLIST = {
    ["application/json"] = true,
  }
}

upstream backend {
  server backend;
}

server {
  listen 80;
  server_name _ default_server;

  location / {
    proxy_pass <http://backend>;

    header_filter_by_lua_block {
      if CONTENT_TYPE_BLACKLIST[ngx.header.content_type] then
        ngx.exit(418)
      end
    }
  }
}

3. 意図した通りの結果が返ってくるかを確認するためのスクリプトを用意する

最後に、意図した通りの結果が返ってくるかを確認するためのスクリプトを用意します。以下がRubyスクリプトの例です。

require 'uri'
require 'net/http'

# 指定したエンドポイントに対してリクエストを送り、ステータスコードが合っているかを確認する
def check_endpoint(url, expected)
  uri = URI.parse(url)
  request = Net::HTTP::Get.new(uri.path)
  response = Net::HTTP.start(uri.host, uri.port) do |http|
    http.open_timeout = 5
    http.read_timeout = 5
    http.request(request)
  end

  if response.code == expected
    puts "OK ... #{url}"
  else
    puts "NG ... #{url}"
    puts "  expected: #{expected}"
    puts "  actual: #{response.code}"
    puts "  body: #{response.body}"
    puts "  headers: #{response.to_hash}"
  end
end

if __FILE__ == $0
  check_endpoint('<http://localhost:3000/>', '200')
  check_endpoint('<http://localhost:3000/api/ping>', '418')
end

結論

以上が、特定のエンドポイントから特定のファイルを返さないようにするために、NginxでContent-Typeを見てエラーを返す手順となります。 この手順を踏むことで、特定のファイルを誤って返さないようにすることができます。