Dragon Arrow written by Tatsuya Nakaji, all rights reserved animated-dragon-image-0164

ActiveStorageにて 「Mixed Content: The page at '<URL>' was loaded over HTTPS, but requested an insecure image '<URL>'. This content should also be served over HTTPS.」

Jul 11, 2019

【Rails 5.2】 Active Storageの混合コンテンツのエラー


環境

Amazon Linux 2
Rails 5.2.1
ruby 2.4.2
(アプリケーション、ウェブサーバー)
nginx version: nginx/1.12.2
unicorn 5.5.1
(ssl証明書)
python2-certbot-nginx 0.34.2-1.el7
certbot 0.34.2-3.el7
certbot-nginx 0.34.2-3.el7


エラー内容

「混合コンテンツ: '<URL>'のページはHTTPS経由でロードされましたが、安全でない画像 '<URL>'を要求しました。このコンテンツもHTTPS経由で配信する必要があります」


原因

ページのURLがhttpsなのに、保証されてないurl(http)がリクエストされていること

=> https://[domain] に アクセスするとhttpsで暗号化により通信が保護されるが、画像のプロトコルがhttpであるため、他者にすり替えられた危険なコンテンツの恐れがある

と言っている


解決策

activeStorageのURL http://[domain]/rails/active_storage/blobs/xxxxxxxxxx/image.jpg を

http => httpsにするだけ...ですがこれがくせもの。


・試したこと(以下どれも効果がなかった)

1: fileアップロード先であるs3にてプロパティ -> static website hosting設定にて
「ウェブサイトのホスティングを無効」 から バケットを指定してhttpsプロトコルにリダイレクト
に変更。

2: Railsのconfig/environments/production.rbにて以下を追記

Rails.application.configure do
  Rails.application.routes.default_url_options[:protocol] = 'https'
  Rails.application.routes.default_url_options[:host] = "www.example.com"
  ...
end

3: activestorageのアップロード先のs3にて アクセス権限->CORSの設定にhttp以外にもhttpsでの通信許可を追加した

<CORSRule>
<AllowedOrigin>https://xxxx</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>*</AllowedHeader>
</CORSRule>

4: config/initializersにforce_ssl.rbというファイルを新規作成
( https://medium.com/@stacietaylorcima/rails-active-storage-serve-your-images-over-https-14b916c67a51 を参考にした)

(force_ssl.rb)
if Rails.application.config.force_ssl  
  Rails.application.routes.default_url_options[:protocol] = 'https'
end


結局プロトコルで矯正すると「「リダイレクトが多すぎます」と表示されてしまう」ためアクセスできなくなるだけだった...

・解決方法

ウェブサーバーにてNginx のリバースプロキシ設定が入ってなかった。

Rails側に過失はなかった。

・Nginx - Rails の場合

NginxでリバースプロキシするときにもSSLはNginxで処理させる場合が多く、プロキシされるアプリケーションサーバにはSSLが解かれた状態でリクエストが届く。
そのため X-Forwarded-Proto ヘッダを使って SSL であることを Railsに伝えなければ、force_ssl が機能しない

# log directory
error_log  /var/www/rails/myapp/log/nginx.error.log; #自分のアプリケーション名に変更
access_log /var/www/rails/myapp/log/nginx.access.log; #自分のアプリケーション名に変更
# max body size
client_max_body_size 2G;
upstream app_server {
  # for UNIX domain socket setups
  server unix:/var/www/rails/myapp/tmp/sockets/unicorn.sock fail_timeout=0; #自分のアプリケーション名に変更
}
server {
  listen 80;
  server_name ~~~.~~~.~~~.~~~;(#アプリのElastic IPに変更してください)
  # nginx so increasing this is generally safe...
  keepalive_timeout 5;
  # path for static files
  root /var/www/rails/myapp/public; #自分のアプリケーション名に変更
  # page cache loading
  try_files $uri/index.html $uri.html $uri @app;
  location @app {
    # HTTP headers
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://app_server;
  }
  # Rails error pages
  error_page 500 502 503 504 /500.html;
  location = /500.html {
    root /var/www/rails/myapp/public; #自分のアプリケーション名に変更
  }
}



以下は非推奨

・臨時の解決方法

結論、画像表示のコードをいじり、絶対パスから相対パスに書き換えて表示させることにしました。

<% @articles.each do |article| %>
  <% if article.image.attached? %>
    - <%= image_tag article.image, :alt => "イメージ", width: '30%', height: '30%' %>
    + <%= image_tag(url_for(article.image), :alt => "イメージ", width: '30%', height: '30%') %>
  <% end %>
<% end %>


出力されてHTMLでいうと、こんな感じになっています

<img alt="イメージ" width="30%" height="30%" src="http://[domain]/rails/active_storage/blobs/xxxxx/sample.jpg">

<img alt="イメージ" width="30%" height="30%" src="/rails/active_storage/blobs/xxxxx/sample.jpg">


絶対パス => 相対パス でプロトコルに左右されないという形にしました。

やむをえずって感じです。