nginxのCORS問題を解決する

localhostでの制作tipsは山ほど出てきたけど
探し方が良くないのかlocalhost-サーバ間の情報って全然見つからない。

## 構成

| key | value |
|---|---|
| 用途 | APIサーバ |
| インスタンス | AWS EC2 |
| SSL | LetsEncrypt (Certbot) |
| Webサーバ | nginx |
| App実行環境 | node.js |
| フレームワーク | express (3000番ポートで稼働) |
| DB | MongoDB |

## nginxのリバースプロキシ,CORS設定
localhost⇔サーバ
サーバ⇔サーバ
におけるnginxの設定は以下で動くのを確認。

```nginx
    map $http_origin $cors{
        "^http://localhost(:\d+)?" $http_origin;
        "^https://chirimen-jako.com" $http_origin;
    }

    server {
        if ($host = api.chirimen-jako.com) {
            return 301 https://$host$request_uri;
        } # managed by Certbot

        listen       80;
        listen       [::]:80;
        server_name  api.chirimen-jako.com;
        return 404; # managed by Certbot
    }

    server {
        server_name  api.chirimen-jako.com;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        error_page 404 /404.html;
            location = /404.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }

        listen [::]:443 ssl ipv6only=on; # managed by Certbot
        listen 443 ssl; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/api.chirimen-jako.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/api.chirimen-jako.com/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

        location / {
            add_header Access-Control-Allow-Origin $cors always;
            add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS' always;
            add_header Access-Control-Allow-Headers 'X-Requested-With,Accept,Content-Type, Origin' always;

            proxy_pass http://localhost:3000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }
    }
```

## （たぶんを含む）解説

### mapディレクティブ

```nginx
    map $http_origin $cors{
        "^http://localhost(:\d+)?" $http_origin;
        "^https://chirimen-jako.com" $http_origin;
    }
```

$cors変数に2種類のoriginをマッピングしている。
if文でlocalhostかサーバか振り分けようとしたがnginxのif文はルールが特殊過ぎてヤバいとのことなのでmapで解決。
機能的に仕様通りではあるがプログラマが通常考えるif文とは違うので..らしい。
[参考サイト](https://mogile.web.fc2.com/nginx_wiki/start/topics/depth/ifisevil/)

### serverディレクティブ(:80)

```nginx
  server {
        if ($host = api.chirimen-jako.com) {
            return 301 https://$host$request_uri;
        } # managed by Certbot

        listen       80;
        listen       [::]:80;
        server_name  api.chirimen-jako.com;
        return 404; # managed by Certbot
    }
```

httpでアクセスがあった場合はs付けてリダイレクト。
SSLを当てる時にCertbotが大体勝手に設定してくれる。

### serverディレクティブ(:443)

```nginx
    server {
        server_name  api.chirimen-jako.com;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        error_page 404 /404.html;
            location = /404.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }

        listen [::]:443 ssl ipv6only=on; # managed by Certbot
        listen 443 ssl; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/api.chirimen-jako.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/api.chirimen-jako.com/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

        location / {
            add_header Access-Control-Allow-Origin $cors always;
            add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS' always;
            add_header Access-Control-Allow-Headers 'X-Requested-With,Accept,Content-Type, Origin' always;

            proxy_pass http://localhost:3000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }
    }
```

locationディレクティブまではほぼデフォルトなので省略。
「# managed by〜」の箇所もCertbotの仕事。
proxy云々の箇所は3000番ポートのアクセスをnodejs(express)へ中継している。
add_headerでレスポンスヘッダにCORS周りの設定を追加している。
ステータスコードが20x/30x系レスポンス以外はadd_headerが効かないとのことなので
alwaysパラメータを付与して必ずヘッダに入るように設定。

**おわり。**