ネイキッドドメイン+HTTPSで運用するRailsアプリを5.1にアップグレードしたら、サブドメインも強制的にHTTPSになってしまった話


## 問題：実際に起きた怖い話

長いタイトルですいません。この問題はちょっとわかりづらいので、まず実例からお話しします。

僕の妻はクープバゲットというパン屋をやっていて、僕はそのWebサイト（メインサイト）を管理しています。
このWebサイトはRailsで構築しています。

https://coupe-baguette.com/

このほかに、ブログサイトがあります。
ブログサイトの実体はTumblrですが、独自ドメインを充てています。

http://blog.coupe-baguette.com/

この時点で押さえておくポイントは以下の2点です。

- メインサイトはネイキッドドメインで、ブログサイトはサブドメインで運用している
- メインサイトはHTTPSで、ブログサイトはHTTPである

今朝、メインサイトのRailsのバージョンを5.0から5.1にアップグレードしました。
メインサイトは順調に動いていたのですが、Railsアップグレードとは無関係なブログサイトが急にアクセスできなくなりました。
以下がそのエラー画面です。

![Screen Shot 2017-07-07 at 9.42.30.png](https://qiita-image-store.s3.amazonaws.com/0/7465/bafee979-3f89-953e-7228-fbbc0308a531.png "Screen Shot 2017-07-07 at 9.42.30.png")

エラーメッセージをよく見ると、ブログサイトのURLが https://blog.coupe-baguette.com になっています。
しかし、ブログサイトはHTTPS対応していないので、当然HTTPSでは接続できません。

でもなんで勝手にHTTPSになってるの！？

・・・というのが、実際に起きた怖い話です。

## 問題の原因：HSTSがサブドメインに対しても有効になったため

ブログサイトが強制的にHTTPS接続になった原因は、メインサイトのレスポンスヘッダにありました。
以下がその問題の行です。

```
Strict-Transport-Security:max-age=15552000; includeSubDomains
```

この行はHSTS(HTTP Strict Transport Security)の設定情報です。
HSTSについては以下の記事で詳しく説明されています。

[HSTS \(HTTP Strict Transport Security\) の導入 \- Qiita](http://qiita.com/takoratta/items/fb6b3486257eb7b9f12e)

簡単にいうと、これはブラウザに対して、

「常にHTTPS接続してくれよ、サブドメインも含めてな！」

とお願いする設定です。
そのため、ブラウザはメインサイトのみならず、ブログサイトまでHTTPSで接続しようとしたのです。

ちなみにRails 5.0時代は以下のようになっていました。

```
Strict-Transport-Security:max-age=15552000
```

ご覧のとおり`includeSubDomains`が付いていないため、ブログサイトはHTTPS接続されることを免れていたのです。

## Rails 5.1からincludeSubDomainsが付くようになった

`includeSubDomains`オプションが付くようになったのは、このコミットの影響です。

https://github.com/rails/rails/commit/4183ee882055f997fe61804ba43ee4c168b2477c

実は`includeSubDomains`オプションは、新規に`rails new`した場合に限り、5.0でも有効になっていました。
なぜなら、`config/initializers/new_framework_defaults.rb`で明示的に設定されているからです。

```ruby:config/initializers/new_framework_defaults.rb
# 省略

# Configure SSL options to enable HSTS with subdomains. Previous versions had false.
Rails.application.config.ssl_options = { hsts: { subdomains: true } }
```

しかし、Rails 4.xからアップグレードした場合はこのオプションが付きません。
そのため、新規に5.0で`rails new`した場合と、4.xからアップグレードしたときで、設定内容が異なることになります。

上記のコミットはこの問題を避けるために、明示的にオプションを指定しなくても、そのバージョンのRailsのデフォルト設定を有効にするための変更です。
その結果、Rails 5.1からは新規に`rails new`した場合と同様に、`includeSubDomains`オプションが付くようになりました。

## もうひとつの条件：force_sslがtrueのときだけHSTSの設定も有効になる

しかし、`rails new`したRails 5.0や、アップグレードしたRails 5.1で必ずHSTSの情報が付与されるわけではありません。

HSTSの情報が付与されるのは`force_ssl`が`true`になっているときだけのようです。

```ruby:config/environments/production.rb
  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
  config.force_ssl = true
```

## 解決策：HSTSのオプションを明示的に上書きする

解決策はデフォルト設定を明示的に上書きすることです。
今回は以前と同じ状態に戻すために、以下のような設定を入れました。

```ruby:config/initializers/ssl.rb
Rails.application.config.ssl_options = { hsts: { subdomains: false } }
```

これで以前と同じように`includeSubDomains`オプションなしのHSTSになりました。

```
Strict-Transport-Security:max-age=15552000
```

なお、`ssl_options`の設定値については以下のAPIドキュメントを参考にしてください。

[ActionDispatch::SSL](http://api.rubyonrails.org/v5.1/classes/ActionDispatch/SSL.html)

## この件のまとめ

というわけで、以下の条件が揃うときっと痛い目を見るよ！

- ネイキッドドメインのサイト（メインサイト）がHTTPS
- サブドメインがサイト（サブサイト）がHTTP
- メインサイトがRailsで構築されている
- メインサイトの`force_ssl`オプションが`true`になっている
- メインサイトをRails 5.1にアップグレードした
- メインサイトにアクセスしてからサブサイトにアクセスする → サブサイトまで強制的にHTTPS接続に:skull::skull::skull:   

もしこの問題が発生したら、Railsの`ssl_options`を明示的に設定して、サブサイトがHTTPS接続されないようにしましょう。

全部の条件が揃うのはかなりレアかもしれませんが、みなさんも注意してくださいね。

### Special thanks to

この件の調査と解決に協力してくれた同僚の @ruzia に感謝します。:pray: 

### あわせて読みたい

この問題とは直接関係ありませんが、妻のパン屋のWebサイトをデザインリニューアルしたときの話をこちらのブログに書いています。

[妻のパン屋のWebサイトを4年ぶりにレスポンシブデザインに作り替えた話 \- give IT a try](http://blog.jnito.com/entry/2017/01/05/071101)[![](http://b.st-hatena.com/entry/image/http://blog.jnito.com/entry/2017/01/05/071101)](http://b.hatena.ne.jp/entry/http://blog.jnito.com/entry/2017/01/05/071101)