LoginSignup
10
11

More than 1 year has passed since last update.

HTTP/3 対応の nginx を作る

Last updated at Posted at 2020-07-21

矛を作らば盾も作らん

そんな格言はないですが、矛となるクライアント(HTTP/3 対応 curl)をせっかく作ったので、盾となるサーバも作ります。

……といっても、やっぱりこのページをなぞっているだけですが
https://github.com/cloudflare/quiche/tree/master/extras/nginx

Cloudflare が作った QUIC の実装である quiche を使います。これを nginx で使えるようにしたパッチも Cloudflare が用意しています。

※ Docker 使えます

その前に。

Docker でお手軽に試せます。
https://hub.docker.com/r/keioni/nginx-http3

導入

quiche は Rust でできているので、Rust が入っていないなら rustup を使って入れます。

他にも、各々の環境にあわせて build に必要なものを入れます。私の場合はこんなパッケージを入れました。

yum install git tar gcc gcc-c++ make cmake3 patch pcre-devel zlib-devel

cmake3 以上が必要なのですが、cmake3 パッケージを入れるとバイナリが cmake3 だったので cmake にシンボリックリンクを張ります。

ln -s /usr/bin/cmake3 /usr/bin/cmake

build

nginx + quiche patch

Cloudflare が用意しているパッチは nginx 1.16.x 系のみの対応です。

curl -O https://nginx.org/download/nginx-1.16.1.tar.gz
tar xzvf nginx-1.16.1.tar.gz

quiche を git から clone してきて、nginx 用のパッチを当てます。

git clone --recursive https://github.com/cloudflare/quiche
cd nginx-*
patch -p01 < ../quiche/extras/nginx/nginx-1.16.patch

build

今回も HTTP/3 対応 curl を作ったときと同じように /opt/http3 以下に入れるようにしました。

./configure \
    --prefix=/opt/http3/nginx \
    --build="quiche-$(git --git-dir=../quiche/.git rev-parse --short HEAD)" \
    --with-http_ssl_module \
    --with-http_v2_module \
    --with-http_v3_module \
    --with-openssl=../quiche/deps/boringssl \
    --with-quiche=../quiche
make
make install

確認

うまくできているか確認します。

/opt/http3/nginx/sbin/nginx -V
nginx version: nginx/1.16.1 (quiche-918c13b)
built by gcc 7.3.1 20180712 (Red Hat 7.3.1-9) (GCC)
built with OpenSSL 1.1.0 (compatible; BoringSSL) (running with BoringSSL)
TLS SNI support enabled
configure arguments: --prefix=/opt/http3/nginx --build=quiche-918c13b --with-http_ssl_module --with-http_v2_module --with-http_v3_module --with-openssl=../quiche/deps/boringssl --with-quiche=../quiche

実行

設定ファイルの準備

証明書と秘密鍵を用意して、以下のように設定ファイルを作ります。

conf/nginx.conf
events {
    worker_connections  1024;
}

http {
    server {
        # Enable QUIC and HTTP/3.
        listen 443 quic reuseport;
        listen [::]:443 quic reuseport;

        # Enable HTTP/2 (optional).
        listen 443 ssl http2;

        location / {
            root   html;
            index  index.html index.htm;
        }

        ssl_certificate      cert.crt;
        ssl_certificate_key  cert.key;

        # Enable all TLS versions (TLSv1.3 is required for QUIC).
        ssl_protocols TLSv1.2 TLSv1.3;

        # Request buffering in not currently supported for HTTP/3.
        proxy_request_buffering off;

        # Add Alt-Svc header to negotiate HTTP/3.
        add_header alt-svc 'h3-29=":443"; ma=86400';
    }
}

起動

/opt/http3/nginx/sbin/nginx

動作確認

socket

netstat -an --udp | grep 443

443/udp が表示されているなら、うまくいっています。

udp        0      0 0.0.0.0:443             0.0.0.0:*
udp6       0      0 :::443                  :::*

curl

以前の記事で作った HTTP/3 対応の curl でアクセスしてみます。

./curl -v --http3 https://localhost/
*   Trying 127.0.0.1:443...
* Connect socket 5 over QUIC to 127.0.0.1:443
* QUIC handshake is completed
* Connected to localhost () port 443 (#0)
* Using HTTP/3 Stream ID: 0 (easy handle 0x137c9e0)
> GET / HTTP/3
> Host: localhost
> user-agent: curl/7.71.1
> accept: */*
>
* ngh3_stream_recv returns 0 bytes and EAGAIN
< HTTP/3 200
< server: nginx/1.16.1
< date: Tue, 21 Jul 2020 02:40:17 GMT
< content-type: text/html
< content-length: 612
< last-modified: Tue, 21 Jul 2020 02:09:03 GMT
< etag: "5f164e3f-264"
< alt-svc: h3-29=":443"; ma=86400
< accept-ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host localhost left intact

ちゃんと HTTP/3 でアクセスできました。いえい。

正式なWebサーバとの同居

すでに別のWebサーバアプリでサービスを提供している場合も、HTTP/3 (QUIC) が使うプロトコル・ポートは 443/udp なので競合せず、一緒に動作させることができます。

設定

設定ファイルをこんな感じにします。

conf/nginx_h3_only.conf

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    server {
        # Enable QUIC and HTTP/3.
        listen 443 quic reuseport;
        listen [::]:443 quic reuseport;

        location / {
            root   html;
            index  index.html index.htm;
        }

        ssl_certificate      cert.crt;
        ssl_certificate_key  cert.key;

        # Enable all TLS versions (TLSv1.3 is required for QUIC).
        ssl_protocols TLSv1.2 TLSv1.3;

        # Request buffering in not currently supported for HTTP/3.
        proxy_request_buffering off;

        # Add Alt-Svc header to negotiate HTTP/3.
        add_header alt-svc 'h3-29=":443"; ma=86400';

        # 他のHTTPSサーバと同居させるときに重要
        add_header last-modified '';
    }
}

元の設定ファイルとの違いは、以下の2つです:

  • listen 443 ssl http2; を取り除く
    これを残したままだと 443/tcp を待ち受けるようになるので、元のWebサーバと競合してしまいます。
  • add_header last-modified ''; を追加
    すでにファイルがある場合、キャッシュの問題が発生しがちなので Last-Modified ヘッダを付けないようにします。このように設定しても ETag があるので、キャッシュはきちんと行われます。

動作確認

再起動したら netstat で確認します。

sudo netstat -an -p --udp --tcp | grep 443

以下のように tcp が、元々使っているWebサーバになっていて1udpnginx になっていれば大丈夫です。

tcp6       0      0 :::443                  :::*                    LISTEN      28911/httpd
udp        0      0 0.0.0.0:443             0.0.0.0:*                           9182/nginx: master
udp6       0      0 :::443                  :::*                                9182/nginx: master

さいごに

HTTP/3 で使うプロトコルの QUIC は、2020年7月27日時点では正式版が出ていません。しかしそう遠くないうちに正式版が出るはずです。

正式版が出る前に、ちょっと試してみるのもいいのでは、と思います。

→ 2021年5月31日に RFC9000 として標準化されました。(2021年8月13日追記)


  1. ここでは httpd なので分かりやすいですが、nginx を使っている場合はPIDの違いで判別してください 

10
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
11