[WordPress][Nginx] Nginx 1.13.9 に実装された http2_push_preload を使って HTTP/2 push してみる

先日リリースされた Nginx ver.1.13.9 に [http2_push](http://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_push) と [http2_push_preload](http://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_push_preload) ディレクティブが実装されました。

> Changes with nginx 1.13.9                                        20 Feb 2018
>
>    *) Feature: HTTP/2 server push support; the "http2_push" and
>       "http2_push_preload" directives.
>
>    *) Bugfix: "header already sent" alerts might appear in logs when using
>       cache; the bug had appeared in 1.9.13.
>
>    *) Bugfix: a segmentation fault might occur in a worker process if the
>       "ssl_verify_client" directive was used and no SSL certificate was
>       specified in a virtual server.
>
>    *) Bugfix: in the ngx_http_v2_module.
>
>    *) Bugfix: in the ngx_http_dav_module.

そんなわけで、WordPress が読み込んでるスタイルシートやら、JavaScript やらを HTTP/2 Push してみようという試み。
<img width="956" alt="スクリーンショット 2018-02-23 10.48.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/25964/68e41c5e-418e-a723-56a2-959f325d8ca3.png">

AMIMOTO AMI を使ってる方は、以下のコマンドで Nginx をバージョンアップしておいてください。

```bash
$ sudo /opt/local/provision
$ nginx -v
nginx version: nginx/1.13.9
```

## Nginx 側の設定変更

`http2_push` ディレクティブを使う場合は、 `server` セクション、もしくは `location` セクションに プッシュしたいファイルを列挙していけばいいです。

```conf
server {
  :
    http2_push /wp-content/themes/twentyseventeen/style.css?ver=4.9.4;
  :
}
```

でも、WordPress 使ってるとテーマ変更したり、プラグイン追加しただけで push したい css や js が変わってきますよね。
それに合わせて一々 nginx.conf 書き換えるだなんて正気の沙汰じゃないです。
ここは `http2_push_preload` ディレクティブを使っていきましょう。

`http2_push_preload` ディレクティブが `on` になっていると(デフォルトは off です)、アプリケーションが送出する `Link` ヘッダを解釈して勝手に HTTP/2 push してくれます。
なので nginx.conf では、以下のように `server` セクションに `http2_push_preload on;` を追加しておくだけです。

```conf
server {
  :
    http2_push_preload on;
  :
}
```

## WordPress で Link ヘッダをだす。

次は、お作法に従って WordPress から適切な `Link` ヘッダを出してあげればいいです。
ちなみに最初のスクリーンショットにあるサイトは以下のような `Link` ヘッダを出してます。

```text
Link: </wp-content/plugins/amazon-polly/public/css/amazonpolly-public.css?ver=1.0.0>; rel=preload; as=style,  </wp-content/themes/twentyseventeen/style.css?ver=4.9.4>; rel=preload; as=style
Link: </wp-content/plugins/amazon-polly/public/js/amazonpolly-public.js?ver=1.0.0>; rel=preload; as=script,  </wp-content/themes/twentyseventeen/assets/js/skip-link-focus-fix.js?ver=1.0>; rel=preload; as=script,  </wp-content/themes/twentyseventeen/assets/js/global.js?ver=1.0>; rel=preload; as=script,  </wp-content/themes/twentyseventeen/assets/js/jquery.scrollTo.js?ver=2.1.2>; rel=preload; as=script
```

`Link` ヘッダの出し方は以下を参考にしてください。
[Preload > Server Push (HTTP/2)](http://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_push)

HTTP/2 push したいときは、こんな感じで `Link` ヘッダを出せって言ってますね。

```text
Link: </app/style.css>; rel=preload; as=style; nopush
Link: </app/script.js>; rel=preload; as=script
```

複数のファイルをプッシュしたいときは、カンマ区切りでも良いとも言ってますね。

```text
Link: </app/style.css>; rel=preload; as=style, </app/script.js>; rel=preload; as=script
```

さて、WordPress では css や js は [wp_enqueue_style()](https://developer.wordpress.org/reference/functions/wp_enqueue_style/), [wp_enqueue_script()](https://developer.wordpress.org/reference/functions/wp_enqueue_script/) で登録するのがお作法です。
真っ当なテーマやプラグインなら、これで登録してるはずなので、この情報を使って Link ヘッダを出してみましょう。
以下のようなプラグインを作って有効にすればオッケーかと思われます。

```php:http2_push.php
<?php
/*
Plugin Name: HTTP/2 Push
Plugin URI: 
Description: HTTP/2 Push
Author: wokamoto
Version: 0.0.1
Author URI: http://dogmap.jp/

License:
 Released under the GPL license
  http://www.gnu.org/copyleft/gpl.html
*/
add_action('template_redirect', function(){
    if ( headers_sent() ) {
        return;
    }

    do_action('wp_enqueue_scripts');

    foreach( array('style' => wp_styles(), 'script' => wp_scripts()) as $as => $wp_links ) {
        $link = '';
        foreach ( $wp_links->queue as $handle ) {
            if ( $wp_links->registered[$handle] && ! preg_match('/-ie8$/i',$handle) && 'html5' !== $handle ) {
                $wp_link = $wp_links->registered[$handle];
                $src = preg_replace('#^https?://[^/]+#', '', $wp_link->src);
                $ver = $wp_link->ver ? $wp_link->ver : $wp_links->default_version;
                $link .= !empty($link) ? ', ' : '';
                $link .= " <{$src}?ver={$ver}>; rel=preload; as={$as}";
                //header("Link: <{$src}?ver={$ver}>; rel=preload; as={$as}", false);
            }
        }
        if ( !empty($link) ) {
            header("Link:{$link}", false);
        }
    }

}, 0, 1);
```

このプラグインの解説は割愛。enqueue されてる css や js を拾ってきて、よしなに `Link` ヘッダを作ってくれるプラグインです。

なお、このプラグインと Nginx 1.13.9 で HTTP/2 push を有効にしたサイトは以下になります。
https://lets.ninja/

Let's Ninja !