こんにちは。ベトナムからエンジニアCCです。
2015末から全て主要なブラウザがhttp2をサポートになりました。
2年前http2を運用しようと思いましたが、色んな疑問がありました。
http2のServer Pushの問題
- railsがhttp2を対応しますか?
- nginxがhttp2を対応しますか?
- メリットってありますか?(Server Pushとか利用できますか?)
 など
特にServer Pushですね。
例えば、nginxじゃなくてh2oを切り替えてmagic的にserver pushを利用できるかと思っていました。
そう出来ればrails側はhttp2を対応しなくてもhttp2のメリットがあるかと思いました。
普通のリクエストは基本には5つのフェーズがあります。

Proxy(nginxやh2o)はhtmlを受けて、処理して以下の状態になります。

で、各フェーズのタイミングを確認したらあまり効果がないかと思っていました。

Download時間だけ節約できましたが、日本のインターネットスピードはめっちゃ早いので、結局何もメリットできない。
理想的にはserver pushはproxyサーバーからじゃなくてアプリから送るのほうがいいではないですか。
(こんな感じ)

そうすと別の問題起こってしまいました。
まずはrailsはhttp2を対応必要。
例えrailsはhttp2を対応しても以下のような形が必要と思いました。

http2自体はsslが必要ので、↑のようアーキテックはめっちゃ複雑になっています。
Rails 5.2
Rails 5.2から HTTP/2 Early Hints を対応くれました 
https://railsguides.jp/5_2_release_notes.html
具体的にはどんな対応だろか?
http/2 early hints対応プルリク: https://github.com/rails/rails/pull/30744
→ 簡単説明すると、javascript_include_tagやstylesheet_link_tagを実行する瞬間に
Link: </style.css>; as=style; rel=preload
のheaderを反します。
これ自体はhttp1だけら以下の設定で動けます 

やってみましょう
ということで実際やってみました。
以下のdocker-composeで試しました。
version: "3.7"
services:
  app:
    build: .
    ports:
      - "3000:3000"
    volumes:
      - ./:/app
      - ./tmp/root/:/root # save .bash_history
    stdin_open: true
    tty: true
  nginx:
    image: nginx:latest
    links:
      - app
    ports:
      - "443:443"
      - "80:80"
    volumes:
      - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./docker/nginx/certs/:/etc/ssl/certs:ro
nginx.conf
    server {
        listen                  443 ssl http2;
        ssl_certificate         /etc/ssl/certs/localhost.crt;
        ssl_certificate_key     /etc/ssl/certs/localhost.key;
        http2_push_preload      on;
        add_header Strict-Transport-Security "max-age=31536000";
        location / {
            proxy_set_header Host $http_host;
            proxy_pass_request_body on;
            proxy_pass_request_headers on;
            proxy_pass http://upstream;
        }
    }
chromeのdevtoolでNetworkタブを見てhttp2を見えました 
ただ、最初のページはスタチックページなのでjavascript_include_tagやstylesheet_link_tagを実行されないです。→ Server pushまだ為できない。
→ controllerを作成して、見たら。。。
ERR_SPDY_PROTOCOL_ERROR エラー出た 
以下のurlでみてエラーログを収集して確認しました
chrome://net-export/
エラーのログ:
{"description":"DATA received before headers.","net_error":"ERR_HTTP2_PROTOCOL_ERROR","stream_id":3}
なぜ?この記事によって1.13.9からhttp2が対応くれるはずなのに、↑のエラー出てしました
https://www.nginx.com/blog/nginx-1-13-9-http2-server-push/
nginx version: nginx/1.17.6
そこで諦めしました。h2oを使って試しました。
h2o.conf
h2o:
    image: lkwg82/h2o-http2-server:latest
    links:
      - app
    ports:
      - "443:1443"
      - "80:8080"
    restart: always
    volumes:
      - ./docker/h2o/h2o.conf:/home/h2o/h2o.conf:ro
      - ./docker/certs/:/etc/ssl/certs:ro

動けました 
結論
- 違いドメインは対応くれないからassetsとhtmlは別originだとpushできません
- 直接htmlを利用しなくて必ずjavascript_include_tagやstylesheet_link_tagを書かないとpushできません
- controllerで遅い処理があったらあまり効果がない、queryとかはviews側で発生したら効果あります
- キャッシュされたリソースがメリットがありません
- なぜかNGINX動きません
- jsとcssしか対応していません(画像やfontまだ対応されていません)
- 
@importとかで書いたらpushできません
- 本番ではダイナミックCSSやダイナミックJavascriptがあったらメリットがあるが、普通はあまりないです。
- 開発環境で使ったらいいだと思います。
