PageSpeed Insightsで100点を取るためにやったこと

# はじめに
内部SEOとして、[PageSpeed Insights](//developers.google.com/speed/pagespeed/insights/)で高得点をとる……なんて話を昔から、チラホラ聞きます。
ページ速度がSEOにどれほどの影響があるのかはわかりませんが、ページ表示速度の改善を行って悪いことはないはずなので、私が運営している個人サイトを[PageSpeed Insights](//developers.google.com/speed/pagespeed/insights/)で100点とれるように頑張ってみました。

本記事では、レンダリングブロック周りを主に取り扱っていきたいと思います。


# 目標
 * デザインは崩さない
     * よく見る対応として、「外部CSSをbodyの直後で読み込むようにして……」というのがあるけど、そうすると、一度CSSが当たらないページが出てしまう。美しくないので、ファーストビューではCSSが当たった状態で表示する
 * maxCDN等を利用している部分は、そのまま使う
   * 個人サイトなので、CDNを使用しているのはサードパーティー製のJSやCSSのみだが、これはこのまま使う
 * 無理をしてJS、CSSをまとめない
   * 外部CSSやJSを無理してまとめると、各ページ毎に作られることになり、ブラウザキャッシュが有効利用されにくい
   * HTTP2にすれば、同時接続の制限もなくなるし……

# 静的コンテンツをキャッシュさせる

画像、JS、CSSは最低2週間、できれば1年間キャッシュさせて下さい。

今回は、3週間にします。

PHPで出力している場合は、

```` .php
<?php
        header('Content-type: text/css');
        header('Cache-Control: max-age='.(60 * 60 * 24 * 21).', public');
        $path = 'xxxxxxx';
        header('Last-Modified: '. date('F d Y H:i:s', filemtime($path)).' JST');
        if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= filemtime($path)) {
            header('HTTP/1.1 304 Not Modified');
            die;
        }
        echo file_get_contents($path);

````

Apacheの場合は、

```` .conf
    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript
    ExpiresActive On
    ExpiresByType text/css "access plus 3 week"
    ExpiresByType text/javascript "access plus 3 week"
    ExpiresByType application/javascript "access plus 3 week"
    ExpiresByType application/x-javascript "access plus 3 week"
    ExpiresByType image/jpeg "access plus 3 week"
    ExpiresByType image/png "access plus 3 week"
    ExpiresByType image/gif "access plus 3 week"
    #フォント
   ExpiresByType application/vnd.ms-fontobject "access plus 3 week"
   ExpiresByType application/x-font-ttf "access plus 3 week"
   ExpiresByType application/x-font-opentype "access plus 3 week"
   ExpiresByType application/x-font-woff "access plus 3 week"
   ExpiresByType image/svg+xml "access plus 3 week"
    FileETag MTime Size
````
こんな感じ。

nginx の場合は、

````
location ~ ^/static_contents/ {
  add_header Cache_Control "max-age=1814400, public";
}
````
こんな感じ。

# レンダリングブロックしているJSをなんとかする

`<script src="..."></script>`はレンダリングブロックするので、[PageSpeed Insights](//developers.google.com/speed/pagespeed/insights/)に怒られます。

これをまずは、なんとかします。

## とりあえず色々省略した元HTML
```` .html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title></title>

    <link rel="stylesheet" as="style" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <link rel="stylesheet" as="style" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
    <link rel="stylesheet" as="style" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/css/bootstrap3/bootstrap-switch.min.css">
    <link rel="stylesheet" as="style" href="//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
    <link rel="stylesheet" as="style" href="//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.css">
    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

  </head>
  <body>

    <div class="container-fluid">
      <div class="row">
        <form class="form-search subscribe-container" method="post" action="" role="form">
            <div class="form-group"> <input type="text" value="" name="xxxxx" class="form-control input-lg url_input" placeholder="入力して下さい。" required> </div>
            <div class="form-group"> <button type="submit" class="btn btn-success btn-lg btn-block">送信</button> </div>
            <div class="form-group"> <label>スイッチ<br><input type="checkbox" value="yes" name="switch_a" class="boot-switch"></label> </div>
        </form>
      </div>
    </div>



    <script>
        $(function() {
            $('form').submit(function() {
                $('form').find(':submit').attr('disabled', 'disabled');
            });
        });
    </script>
  

    <script src="//netdna.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>

    <script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/js/bootstrap-switch.min.js"></script>
    <script>
    $('.boot-switch').bootstrapSwitch();
    </script>

    <script src="//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.js"></script>

  </body>
</html>
````

## [PageSpeed Insights](//developers.google.com/speed/pagespeed/insights/)の解析結果
<img width="1068" alt="7a5a22fc1ce384ebcb386a264587aa11.png" src="https://qiita-image-store.s3.amazonaws.com/0/144041/57571dc3-98aa-3d1c-4774-31a6abaf2632.png">



解決方法としては、２つあり、`async`を付けるか、`defer`を付けるかのどっちかになります。

 * `async`
   * 非同期でJSを読み込み、可能な限りはやく実行
 * `defer`
   * 非同期でJSを読み込み、ページを最後まで読み込んだら順番に実行

`async`は、読み込まれると同時に実行されますので、実行順序は保証されません。
単体で解決しているならともかく、依存がある場合は使用できません。

`defer`はページが読み込み終わるまで実行されませんが、実行順序は保証されます。


__JQuery__使ってるし、__Bootstrap__も使ってるので、`defer`一択です。

読み込んでいる、Script全部に、`defer`を付けたら、実行順序も保証されるし、そのまま動くよね！


<img width="513" alt="7fcf3bd5503b7089bfbd2d42e347ec5f.png" src="https://qiita-image-store.s3.amazonaws.com/0/144041/e76d2222-4cfa-3a40-4c47-e98b69c3786d.png">

orz...動きませんでした。

`defer`すると、インラインで記述された依存コードが動かなくなります。

全部外に出すのはめんどくさいので、色々試してみたところ、

```` .html
    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js" onload="
        $(function() {
            $('form').submit(function() {
                $('form').find(':submit').attr('disabled', 'disabled');
            });
        });
    " defer></script>
````
こんな感じで、scriptタグのonloadイベントの中に依存コードを入れれば、解決するっぽいです。

※あと、外部JSの中でdocument.writeしている場合もNGのようです。


## 変更後HTML
```` .html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title></title>

    <link rel="stylesheet" as="style" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <link rel="stylesheet" as="style" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
    <link rel="stylesheet" as="style" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/css/bootstrap3/bootstrap-switch.min.css">
    <link rel="stylesheet" as="style" href="//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
    <link rel="stylesheet" as="style" href="//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.css">

  </head>
  <body>

    <div class="container-fluid">
      <div class="row">
        <form class="form-search subscribe-container" method="post" action="" role="form">
            <div class="form-group"> <input type="text" value="" name="xxxxx" class="form-control input-lg url_input" placeholder="入力して下さい。" required> </div>
            <div class="form-group"> <button type="submit" class="btn btn-success btn-lg btn-block">送信</button> </div>
            <div class="form-group"> <label>スイッチ<br><input type="checkbox" value="yes" name="switch_a" class="boot-switch"></label> </div>
        </form>
      </div>
    </div>



    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js" onload="
        $(function() {
            $('form').submit(function() {
                $('form').find(':submit').attr('disabled', 'disabled');
            });
        });
    " defer></script>
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js" defer></script>

    <script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/js/bootstrap-switch.min.js" onload="$('.boot-switch').bootstrapSwitch();" defer></script>

    <script src="//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.js" defer></script>

  </body>
</html>
````

点数は変わりませんが、JSのエラーは消えました。

<img width="1144" alt="9ec682d1a3fb6bb6c9d69c6e2e21e86f.png" src="https://qiita-image-store.s3.amazonaws.com/0/144041/5a0ef687-12cc-839f-85e4-81df9b9837fa.png">


# CSSを遅延レンダリングさせる

CSSはHTML単体で、どうにかする方法はないので、AsyncとDeferを行う事ができる、JSを作ってみました。

DLはこちら⇒__[PrefetchLoader](https://github.com/suzunone/PrefetchLoader)__


使い方は簡単。
今回の場合であれば、

````.html
<script src="/js/PrefetchLoader.js" async onload="
(function () {
var prefetch = new PrefetchSequence(
[
    {
        urls: [
            '//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css',
            '//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css',
            '//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/css/bootstrap3/bootstrap-switch.min.css',
        ],
        default_resource: 'style',
        using_defer_css_load: true // deferで読み込む
    },
    {
        urls: [
            '//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css',
            '//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.css',
        ],
        default_resource: 'style',
        using_async_css_load: true // asyncで読み込む
    }
]


);
prefetch.allAsync();


}());


 "></script>
 <noscript>
    <link rel="stylesheet" as="style" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <link rel="stylesheet" as="style" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
    <link rel="stylesheet" as="style" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/css/bootstrap3/bootstrap-switch.min.css">
    <link rel="stylesheet" as="style" href="//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
    <link rel="stylesheet" as="style" href="//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.css">
 </noscript>

````

このJSは、単独で動作するので、asyncで読み込みます。

やってることは、
preloadもしくはprefetchを利用してCSSを非同期で読み込み、読み込み終わったら、レンダリングさせているだけです。

これでCSSの遅延読み込みは完了！

<img width="396" alt="9b744b189450c1b48fd4ca64c4aad556.png" src="https://qiita-image-store.s3.amazonaws.com/0/144041/57db1762-de1b-9c13-eb5d-d6c483270d1b.png">


と思いきや、デザインが崩れてしまいました。

どうやら、CSSを非同期にしたことで、CSSに依存したインラインJSがおかしくなったようです。

具体的には

````.html
    <script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/js/bootstrap-switch.min.js" onload="$('.boot-switch').bootstrapSwitch();" defer></script>
````

これ。ここの部分。

これを解決するには、CSSが適応されてから、onloadに指定したJSを実行してやればいいので、


````.html
  <script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/js/bootstrap-switch.min.js" defer onLoad="
    window.addEventListener('load', function(){
      $('.boot-switch').bootstrapSwitch();
    }, false );
  "></script>
````

こんな感じで書けば、問題なくなった。

もしくは、__[PrefetchLoader](https://github.com/suzunone/PrefetchLoader)__の`preload_success`でやるか。

<img width="1103" alt="a67404f6b119dec90329e426967aa6e0.png" src="https://qiita-image-store.s3.amazonaws.com/0/144041/adba2aa7-211d-13df-8d59-c2260a02d788.png">

……あれ？
むしろ点数下がっているんですが……？


しかも、CSSのレンダリングブロックが解除されていないし、なんかエラーも増えてる。

そして、増えたエラーメッセージはこれ。

<img width="807" alt="c3e0f00b33adfbc75dc034ac05b094e0.png" src="https://qiita-image-store.s3.amazonaws.com/0/144041/9c6176ba-6b20-beb8-62b2-4a399897d264.png">

エラーメッセージを読むと、
> 非同期読み込んでるCSSってやっぱりレンダリングに必要だったんじゃない？
> 5%しかレンダリングできなかったよ？

ってことですね。

図らずも、目標として掲げた、___「ファーストビューでキチンとstyleを当てる」___という事をやれば、解決しそうですね。

とはいえ、使用しているCSSをすべてインラインにするのは現実的ではありません。
ファーストビューで、適応されているCSSだけ抜き取ってインラインCSSにしたい。

そこで、[gulp-uncss](https://www.npmjs.com/package/gulp-uncss)を使います。

npmがインストールされていれば、

↓のpackage.jsonを配置して、`npm install`するだけ。

```` package.json
{
  "name": "",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": ""
  },
  "keywords": [
  ],
  "author": "Suzunone",
  "license": "",
  "homepage": "",
  "devDependencies": {
    "gulp": "^3.9.1",
    "gulp-uncss": "^1.0.6"
  }
}
````

つぎに、ページで読み込まれているCSSすべて一纏めにして、package.jsonを配置したディレクトリに、__site.css__として配置します。

そして、以下のgulpfile.jsを、同じディレクトリに配置します。

```` gulpfile.js
var gulp = require('gulp');
var uncss = require('gulp-uncss');

gulp.task('default', function () {
    return gulp.src('site.css')
        .pipe(uncss({
            html: ['https://xxxxxxxxx/test.html'] //ここは任意のurlにして下さい
        }))
        .pipe(gulp.dest('./out/'));
});
````

ディレクトリ構造は、こんな感じになります。

````
├── gulpfile.js
├── node_modules
├── package.json
└── site.css
````

配置し終わったら、

`gulp`

とコマンドを実行すると、ファーストビューで使用されるCSSだけが、outディレクトリに書き出されます。

````
├── gulpfile.js
├── node_modules
├── out
│   └── site.css
├── package.json
└── site.css
````

書き出されたCSSを<head>内に、インラインで記述する。

````.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title></title>


<script src="/js/PrefetchLoader.js" async onload="
(function () {
var prefetch = new PrefetchSequence(
[
    {
        urls: [
            '//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css',
            '//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css',
            '//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/css/bootstrap3/bootstrap-switch.min.css',
        ],
        default_resource: 'style',
        using_defer_css_load: true
    },
    {
        urls: [
            '//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css',
            '//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.css',
        ],
        default_resource: 'style',
        using_async_css_load: true
    }
]


);
prefetch.allAsync();


}());


 "></script>
 <noscript>
    <link rel="stylesheet" as="style" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <link rel="stylesheet" as="style" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
    <link rel="stylesheet" as="style" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/css/bootstrap3/bootstrap-switch.min.css">
    <link rel="stylesheet" as="style" href="//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
    <link rel="stylesheet" as="style" href="//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.css">
 </noscript>
<style>
html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}button,input{margin:0;font:inherit;color:inherit}button{overflow:visible}button{text-transform:none}button{-webkit-appearance:button;cursor:pointer}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input{font-family:inherit;font-size:inherit;line-height:inherit}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=checkbox]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=checkbox]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-group{margin-bottom:15px}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active:focus,.btn-success:active:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success:active{background-image:none}.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-block{display:block;width:100%}.container-fluid:after,.container-fluid:before,.row:after,.row:before{display:table;content:" "}.container-fluid:after,.row:after{clear:both}@-ms-viewport{width:device-width}
.btn-success{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-success:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn:active{background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success:active{background-color:#419641;border-color:#3e8f3e}
 body{position:relative}body{padding-top:50px}.url_input{border:1px solid #FF8000}
.bootstrap-switch{display:inline-block;direction:ltr;cursor:pointer;border-radius:4px;border:1px solid #ccc;position:relative;text-align:left;overflow:hidden;line-height:8px;z-index:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.bootstrap-switch .bootstrap-switch-container{display:inline-block;top:0;border-radius:4px;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on,.bootstrap-switch .bootstrap-switch-label{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;cursor:pointer;display:inline-block!important;height:100%;padding:6px 12px;font-size:14px;line-height:20px}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on{text-align:center;z-index:1}.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary{color:#fff;background:#337ab7}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default{color:#000;background:#eee}.bootstrap-switch .bootstrap-switch-label{text-align:center;margin-top:-1px;margin-bottom:-1px;z-index:100;color:#333;background:#fff}.bootstrap-switch .bootstrap-switch-handle-on{border-bottom-left-radius:3px;border-top-left-radius:3px}.bootstrap-switch .bootstrap-switch-handle-off{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch input[type=checkbox]{position:absolute!important;top:0;left:0;margin:0;z-index:-1;opacity:0;filter:alpha(opacity=0)}.bootstrap-switch.bootstrap-switch-off .bootstrap-switch-label{border-bottom-left-radius:3px;border-top-left-radius:3px}
</style>

  </head>
  <body>

    <div class="container-fluid">
      <div class="row">
        <form class="form-search subscribe-container" method="post" action="" role="form">
            <div class="form-group"> <input type="text" value="" name="xxxxx" class="form-control input-lg url_input" placeholder="入力して下さい。" required> </div>
            <div class="form-group"> <button type="submit" class="btn btn-success btn-lg btn-block">送信</button> </div>
            <div class="form-group"> <label>スイッチ<br><input type="checkbox" value="yes" name="switch_a" class="boot-switch"></label> </div>
        </form>
      </div>
    </div>



    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js" onload="
        $(function() {
            $('form').submit(function() {
                $('form').find(':submit').attr('disabled', 'disabled');
            });
        });
    " defer></script>
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js" defer></script>

  <script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/js/bootstrap-switch.min.js" defer onLoad="
    window.addEventListener('load', function(){
      $('.boot-switch').bootstrapSwitch();
    }, false );
  "></script>

    <script src="//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.js" defer></script>

  </body>
</html>
````

<img width="1147" alt="370ecc7967efee506783d7d8e9469c87.png" src="https://qiita-image-store.s3.amazonaws.com/0/144041/9d5f70f5-6c36-9605-387f-40a77bf91a55.png">
<img width="1154" alt="83913086c21effb4b4566715c9bae436.png" src="https://qiita-image-store.s3.amazonaws.com/0/144041/c7fabfdd-ac5d-9a4c-da62-d407a62d12cc.png">

やったぜ。

# 画像を圧縮(最適化)する
幸いながら、動的に画像が増えるような機能はないため、皆大好き[tinypng](https://tinypng.com/)(怪しいパンダのアイツ)で何とかなりました。

動的に最適化する場合は、[Google推奨の方法](https://developers.google.com/speed/docs/insights/OptimizeImages)で対応して下さい。

# 最後に
実際には、サードパーティー製の解析タグや、広告タグがあり、100点を取ることは不可能かも知れませんが、かなり近づけることはできると思います。
そして、キチンと対応すると、確かに表示速度は速くなりました。

サーバーのレスポンスタイムは変わらなくても、体感的に早くなった気がします。
（プラシーボ効果かもしれませんが）


今回作った、__[PrefetchLoader](https://github.com/suzunone/PrefetchLoader)__.jsは、CSSの遅延ロード以外にもコンテンツの先読みに使用できますので、より一層の早さを目指すなら、

```` .js
(function () {
var prefetch = new PrefetchSequence(
[
    {
        urls: [
            '//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css',
            '//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css',
            '//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/css/bootstrap3/bootstrap-switch.min.css',
        ],
        default_resource: 'style',
        using_defer_css_load: true
    },
    {
        urls: [
            '//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css',
            '//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.css',
        ],
        default_resource: 'style',
        using_async_css_load: true
    },
    {
        urls: [
            '//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js',
            '//netdna.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js',
            '//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/js/bootstrap-switch.min.js',
            '//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.js'
        ],
        default_resource: 'script',
    }
]

]


);
prefetch.allAsync();


}());
````

こんな感じで、HTMLの最後で読み込まれるJSを先読みさせたり、


画像のlazyLoadを行っている場合に、

```` .js
window.addEventListener( 'load', function(){
    var prefetch = new PrefetchLoader({
        href_attribute_name:'data-src'
    });
    prefetch.all('img[data-src]');


}, false );
````

こんな感じで、通信の余剰を使って、先に画像を読み込んだりできます。

是非試してみて下さい。

プルリクもお待ちしております。


# 更新履歴
 * Apacheの設定にフォントの設定が抜けていました