【完全無料でSSL化】RailsアプリをNginxで動かす!Nginxのインストール方法から最低限しておきたい設定まで

RailsアプリをNginxで動かすために、Nginxをサーバーに導入する方法と最低限これだけはしておきたいという設定をまとめます。
  • ローカル環境:macOS Catalina
  • サーバー環境:CentOS 7.7

Nginxインストール

ターミナルで以下のコマンドを実行し、サーバーにSSH接続します。
SSH接続の設定は事前に行っておいてください。
$ ssh server
Enter passphrase for key '/Users/user/.ssh/server/id_rsa':
サーバーのユーザーディレクトリ直下にインストールします。
$ cd ~
$ sudo yum install nginx
[sudo] user のパスワード:
...
バージョンが表示されればインストール完了です。
$ nginx -v
nginx version: nginx/1.16.1

自動起動設定

サーバーの起動時にNginxを自動起動するように設定します。
$ chkconfig nginx on
情報:'systemctl enable nginx.service'へ転送しています。
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-unit-files ===
Authentication is required to manage system service or unit files.
Authenticating as: root
Password: 
==== AUTHENTICATION COMPLETE ===
==== AUTHENTICATING FOR org.freedesktop.systemd1.reload-daemon ===
Authentication is required to reload the systemd state.
Authenticating as: root
Password: 
==== AUTHENTICATION COMPLETE ===
OSの設定によっては一部の文章が赤文字でびっくりしますが、2回パスワードを入力して設定完了です。

SSL証明書作成

Let's Encryptを使って無料のSSL証明書を作成します。
まず、Let's Encryptのクライアントである「cerbot」をインストールします。
$ cd ~
$ git clone https://github.com/certbot/certbot
ヘルプが表示されればインストール完了です。
$ cd certbot
$ ./certbot-auto --help
Usage: certbot-auto [OPTIONS]
A self-updating wrapper script for the Certbot ACME client. When run, updates
to both this script and certbot will be downloaded and installed. After
ensuring you have the latest versions installed, certbot will be invoked with
all arguments you have provided.

Help for certbot itself cannot be provided until it is installed.

  --debug                                   attempt experimental installation
  -h, --help                                print this help
  -n, --non-interactive, --noninteractive   run without asking for user input
  --no-self-upgrade                         do not download updates
  --os-packages-only                        install OS dependencies and exit
  -v, --verbose                             provide more output
SSL証明書を作成する前に、Nginxを起動している場合は事前に停止させておきます。
スタンドアローンモードのLet's Encryptは自前のHTTPサーバーを起動して証明書の取得を行おうとするのですが、既にNginxを起動していてTCP/80が使用中だとLet's EncryptのHTTPサーバーが起動(TCP/80のバインド)ができないというエラーが出てしまいます。
Nginxを停止するには以下のコマンドを実行します。
# service nginx stop
ようやくSSL証明書を作成します。以下のコマンドを実行します。
$ ./certbot-auto certonly --standalone -t
Requesting to rerun ./certbot-auto with root privileges...
[sudo] user のパスワード:
certbot-autoコマンドの初回実行時は不足パッケージのインストールが始まります。
不足パッケージのインストールが完了したらメールアドレスを聞かれます。確認メールがくるわけではありませんが、すぐに確認できるメールアドレスを入力しましょう。
Creating virtual environment...
Installing Python packages...
Installation succeeded.
Enter email address (used for urgent notices and lost key recovery) (Enter 'c' to cancel): user@example.com
次にライセンスに同意するか聞かれるので同意しましょう。
-------------------------------------------------------------------------------
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf. You must agree
in order to register with the ACME server at
https://acme-v01.api.letsencrypt.org/directory
-------------------------------------------------------------------------------
(A)gree/(C)ancel: A
次にSSL証明書を作成するドメイン名を聞かれます。
ドメイン名は名前解決できる必要があるので、DNSに登録していない場合は事前に登録しておきましょう。
また、ここで入力するドメイン名は「サブドメインあり/なし」が区別されます。例えば「www.example.com」で作成したSSL証明書は「example.com」では使えないので注意しましょう。
Please enter in your domain name(s) (comma and/or space separated)  (Enter 'c' to cancel):www.example.com
以下のメッセージが表示されたら完了です。
登録したメールアドレスに完了メールが届いているかと思います。
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/www.hogehoge.com/fullchain.pem. Your cert
   will expire on 2016-11-11. To obtain a new or tweaked version of
   this certificate in the future, simply run certbot-auto again. To
   non-interactively renew *all* of your certificates, run
   "certbot-auto renew"
 - If you lose your account credentials, you can recover through
   e-mails sent to user@example.com.
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le
最後に、SSL証明書を自動で更新する設定をしておきます。
管理者ユーザーのcronに設定します。
$ su -
パスワード:
# crontab -e
0 0 1 * * /home/user/certbot/certbot-auto certonly --webroot -w /var/www/app -d www.example.com --agree-tos --force-renewal -n && nginx -s reload

設定ファイル

ここでいう設定ファイルとは、Railsアプリ固有の設定ファイルになります。
Nginx本体の設定ファイル(/etc/nginx/nginx.conf)から固有の設定ファイルが読み込まれるという仕組みになっています。
$ cat /etc/nginx/nginx.conf
...
    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;
...
設定ファイルを作成する前に管理者ユーザーに変更しておきます。基本的にNginxに関する操作をするときは管理者ユーザーで行います。
$ su -
パスワード:
以下のコマンドを実行して設定ファイルを作成します。
ファイル名は拡張子が「.conf」であれば何でもいいです。
# cd /etc/nginx/conf.d
# touch app.conf
# ls -l
total 4
-rw-r--r-- 1 root root 1391 Dec  4 17:00 app.conf
設定ファイルの全文は以下の通りです。
ディレクトリ名などを変更すればそのままコピペ可能です。
error_log  /var/www/app/current/log/nginx.error.log;
access_log /var/www/app/current/log/nginx.access.log;
client_max_body_size 2G;
upstream app_server {
  server unix:/var/www/app/current/tmp/sockets/.unicorn.sock fail_timeout=0;
}
server {
  listen 80;
  server_name www.example.com;
  return 301 https://$host$request_uri;
}
server {
  listen 80;
  listen 443;
  server_name example.com;
  return 301 https://www.$host$request_uri;
}
server {
  listen 443 ssl default_server;
  ssl_certificate     /etc/letsencrypt/live/www.example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem;
  server_name www.example.com;
  keepalive_timeout 5;
  root /var/www/app/current/public;
  try_files $uri/index.html $uri.html $uri @app;
  location @app {
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_redirect off;
    proxy_pass http://app_server;
  }
  error_page 500 502 503 504 /500.html;
  location = /500.html {
    root /var/www/app/current/public;
  }
  location = /robots.txt {
    alias /var/www/app/current/public/robots.txt;
  }
}
「HTTP (TCP/80) かつ サブドメインあり」「HTTP (TCP/80) かつ サブドメインなし」「HTTPS (TCP/443) かつ サブドメインなし」でアクセスされた場合、「HTTPS (TCP/443) かつ サブドメインあり」にリダイレクトする設定をしています。
具体的には以下のとおりです。
  • http://example.com/
  • http://www.example.com/
  • https://example.com/
   ↓
  • https://www.example.com/

また、Let's Encryptを使って作成したSSL証明書を指定しています。これを設定しないとアクセスした際に警告が表示されるようになってしまいます。

ポート確認

HTTP (TCP/80)とHTTPS (TCP/443)が開放されているか確認します。
確認にはnmapを使うのでインストールしましょう。
$ sudo yum install nmap
[sudo] user のパスワード:
...
==================================================================================================================================================================================
 Package                                    アーキテクチャー                        バージョン                                        リポジトリー                           容量
==================================================================================================================================================================================
インストール中:
 nmap                                       x86_64                                  2:6.40-19.el7                                     base                                  3.9 M
依存性関連でのインストールをします:
 nmap-ncat                                  x86_64                                  2:6.40-19.el7                                     base                                  206 k

トランザクションの要約
==================================================================================================================================================================================
インストール  1 パッケージ (+1 個の依存関係のパッケージ)

総ダウンロード容量: 4.2 M
インストール容量: 17 M
Is this ok [y/d/N]: y
...
インストール:
  nmap.x86_64 2:6.40-19.el7                                                                                                                                                       

依存性関連をインストールしました:
  nmap-ncat.x86_64 2:6.40-19.el7                                                                                                                                                  

完了しました!
インストールが完了したら以下のコマンドを実行します。
$ nmap xxx.xxx.xxx.xxx

Starting Nmap 6.40 ( http://nmap.org ) at 2019-12-05 09:57 JST
Nmap scan report for vxxx-xxx-xxx-xxx.a091.g.tyo1.static.cnode.io (xxx.xxx.xxx.xxx)
Host is up (0.00026s latency).
Not shown: 996 closed ports
PORT     STATE SERVICE
80/tcp   open  http
111/tcp  open  rpcbind
443/tcp  open  https
3306/tcp open  mysql

Nmap done: 1 IP address (1 host up) scanned in 0.05 seconds
HTTP (TCP/80)とHTTPS (TCP/443)のポートが開放されていることが確認できました。

Nginx起動

さて、いよいよNginxを起動します。
しかしその前に、念のためNginxの設定ファイルが間違っていないかを確認しておきます。設定ファイルが間違っていなければ以下のように表示されます。
# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
設定ファイルが間違っている場合、以下のように表示されます。
例は9行目の「server_name:」が間違っている場合です。正しくは「server_name」(コロンは不要)。
# nginx -t
nginx: [emerg] unknown directive "server_name:" in /etc/nginx/conf.d/app.conf:9
nginx: configuration file /etc/nginx/nginx.conf test failed
Nginxを起動するには以下のコマンドを実行します。
# service nginx start
正常に起動できたか状態が確認したいときは以下のコマンドを実行します。
# service nginx status
Redirecting to /bin/systemctl status nginx.service
● nginx.service - The nginx HTTP and reverse proxy server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
   Active: active (running) since Mon 2019-12-02 22:54:13 JST; 37min ago
  Process: 12067 ExecReload=/bin/kill -s HUP $MAINPID (code=exited, status=0/SUCCESS)
  Process: 12437 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS)
  Process: 12434 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS)
  Process: 12433 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, status=0/SUCCESS)
 Main PID: 12439 (nginx)
   CGroup: /system.slice/nginx.service
           ├─12439 nginx: master process /usr/sbin/nginx
           └─12440 nginx: worker process

Dec 02 22:54:13 150-95-152-189 systemd[1]: Starting The nginx HTTP and reverse proxy server...
Dec 02 22:54:13 150-95-152-189 nginx[12434]: nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
Dec 02 22:54:13 150-95-152-189 nginx[12434]: nginx: configuration file /etc/nginx/nginx.conf test is successful
Dec 02 22:54:13 150-95-152-189 systemd[1]: Failed to parse PID from file /run/nginx.pid: Invalid argument
Dec 02 22:54:13 150-95-152-189 systemd[1]: Started The nginx HTTP and reverse proxy server.

Railsアプリの設定

config/environments/production.rbの設定を以下の通り変更します。
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = true
コメントアウトを外して強制SSL化設定を有効にします。なければ普通に追加すればいいと思います。

まとめ

いかがでしたでしょうか。
初めからすべてを設定しようとすると手順が多くなってしまって大変かと思います。SSL証明書の導入などは後回しにしてしまってもいいかもしれません。
間違いなどありましたらご指摘いただけると助かります。

おまけ:よく使うNginxのコマンド

Nginxの停止・起動・再起動。
# service nginx stop
# service nginx start
# service nginx reload
Nginxの状態確認。
# service nginx status
Redirecting to /bin/systemctl status nginx.service
● nginx.service - The nginx HTTP and reverse proxy server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
   Active: active (running) since Mon 2019-12-02 22:54:13 JST; 37min ago
  Process: 12067 ExecReload=/bin/kill -s HUP $MAINPID (code=exited, status=0/SUCCESS)
  Process: 12437 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS)
  Process: 12434 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS)
  Process: 12433 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, status=0/SUCCESS)
 Main PID: 12439 (nginx)
   CGroup: /system.slice/nginx.service
           ├─12439 nginx: master process /usr/sbin/nginx
           └─12440 nginx: worker process

Dec 02 22:54:13 150-95-152-189 systemd[1]: Starting The nginx HTTP and reverse proxy server...
Dec 02 22:54:13 150-95-152-189 nginx[12434]: nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
Dec 02 22:54:13 150-95-152-189 nginx[12434]: nginx: configuration file /etc/nginx/nginx.conf test is successful
Dec 02 22:54:13 150-95-152-189 systemd[1]: Failed to parse PID from file /run/nginx.pid: Invalid argument
Dec 02 22:54:13 150-95-152-189 systemd[1]: Started The nginx HTTP and reverse proxy server.
Nginxの設定ファイルがあっているか確認。
# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful