Apache をバックエンドにして nginx をリバースプロキシにする

## nginx の問題点
高機能が故に重くなってしまった Apache HTTP Server（以下 Apache）と比べて、nginx は軽量な Web サーバーとして近年 Apache よりもシェアを伸ばしている。

しかし nginx の欠点として、これまで Apache でよく利用されてきた .htaccess ファイルによるリダイレクトやベーシック認証の設定が行えないという問題点がある。

この問題点を解決する方法として、Apache をリバースプロキシの参照先として利用することでフロントを nginx にしたまま .htaccess ファイルによるリダイレクトやベーシック認証の設定が行えるようになる。

## Apache と nginx のセットアップ
### 環境
* AlmaLinux 9.5
* Apache 2.4.62
* nginx 1.20.1

### インストール
```bash
dnf install httpd nginx
```

### サービスの確認
```bash
systemctl is-enabled httpd nginx
```
```
disabled
disabled
```
:::note
インストール時はサービスが有効（ブート時起動）になっていない。
:::

```bash
systemctl status httpd nginx
```
```
○ httpd.service - The Apache HTTP Server
     Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; preset: disabled)
     Active: inactive (dead)
       Docs: man:httpd.service(8)

○ nginx.service - The nginx HTTP and reverse proxy server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; preset: disabled)
     Active: inactive (dead)
```
:::note
インストール時はサービスが実行（起動）されていない。
:::

### Apache の設定
#### ポート番号変更設定
```bash
echo 'Listen 8080' >> /etc/httpd/conf.d/reverse-proxy.conf
echo 'ServerName localhost:8080' >> /etc/httpd/conf.d/reverse-proxy.conf
```
:::note
8080 ポートはサービス名 http-alt で HTTP Alternate として公式に登録されている。 [^port8080]
:::

[^port8080]: <cite>[Service Name and Transport Protocol Port Number Registry](https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=http-alt) - <i>www.iana.org</i></cite>

[^listen]: <q>`Listen` ディレクティブは Apache が特定の IP アドレスやポート番号だけを listen するように指定します。</q><cite>[mpm_common - Apache HTTP サーバ バージョン 2.4](https://httpd.apache.org/docs/2.4/ja/mod/mpm_common.html#listen) - <i>httpd.apache.org</i></cite>

#### 80 番ポートを閉じる
```bash
sed --expression='/^Listen/ s/^/#/' --in-place /etc/httpd/conf/httpd.conf
```

:::note warn
デフォルトの conf/httpd.conf で指定されている `Listen 80` をコメントアウトしないと Apache が80番ポートを占有してしまい、nginx が以下のエラーを返して起動しない。
```
nginx: [emerg] bind() to 0.0.0.0:80 failed (XX: Address already in use)
```
:::

:::note
`ServerName` ディレクティブが指定されていないと以下のエラーメッセージが表示される。
> httpd: Could not reliably determine the server's fully qualified domain name, using xxxx::xxx:xxxx:xxxx:xxxx%enp0s3. Set the 'ServerName' directive globally to suppress this message
:::


### ポートの開放
8080 ポートは firewalld に登録されていないので、 `firewall-cmd --new-service` コマンドで追加する。 [^new-service]
```bash
firewall-cmd --permanent --new-service=http-alt
firewall-cmd --permanent --service=http-alt --set-short='WWW Alternate (HTTP)'
firewall-cmd --permanent --service=http-alt --set-description='HTTP Alternate. HTTP is the protocol used to serve Web pages.'
firewall-cmd --permanent --service=http-alt --add-port=8080/tcp
firewall-cmd --permanent --add-service=http-alt
firewall-cmd --reload
firewall-cmd --list-services
```

[^new-service]: <q>To add a new and empty service, use the `--new-service` altogether with the `--permanent` option:</q><cite>[Documentation - HowTo - Add a Service | firewalld](https://firewalld.org/documentation/howto/add-a-service.html) - <i>firewalld.org</i></cite>

### nginx の設定
```nginx:conf.d/reverse-proxy.conf
server {
    listen 80;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        real_ip_header X-Forwarded-For;
    }
}
```
`proxy_pass` [^proxy_pass]

nginx はデフォルトでは HTTP/1.0 を使用してプロキシリクエストを行うため、`proxy_http_version` で `1.1` を指定してやる必要がある。 [^proxy_http_version]

[^proxy_pass]: <q>Sets the protocol and address of a proxied server and an optional URI to which a location should be mapped. As a protocol, “`http`” or “`https`” can be specified. The address can be specified as a domain name or IP address, and an optional port:</q><q>or as a UNIX-domain socket path specified after the word “`unix`” and enclosed in colons:</q><cite>[Module ngx_http_proxy_module](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass) - <i>nginx.org</i></cite>

[^proxy_http_version]: <q>Sets the HTTP protocol version for proxying. By default, version 1.0 is used. Version 1.1 is recommended for use with [keepalive](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive) connections and [NTLM authentication](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#ntlm).</q><q>This directive appeared in version 1.1.4.</q><cite>[Module ngx_http_proxy_module](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_http_version) - <i>nginx.org</i></cite>

### SELinux の設定変更
```bash
setsebool -P httpd_can_network_relay 1
```

:::note warn
SELinux が有効な場合、上記の設定を変更しないと以下のエラーが出て 502 Bad Gateway が返される。
```
connect() to 127.0.0.1:8080 failed (13: Permission denied) while connecting to upstream`
```
:::

:::note
`httpd_can_network_connect` を on にする記事が多く、それで実際に動作するが、 `httpd_can_network_relay` の方が本来的でよりセキュア。[^httpd_can_network_connect][^httpd_can_network_relay]
```
setsebool -P httpd_can_network_connect 1
```
:::

[^httpd_can_network_connect]: <q>このブール値を無効にすると、HTTP スクリプトおよびモジュールは、ネットワークまたはリモートポートへの接続を開始できなくなります。このアクセスを許可するには、このブール値を有効にします。</q><cite>[13.3. ブール値 | Red Hat Product Documentation](https://docs.redhat.com/ja/documentation/red_hat_enterprise_linux/7/html/selinux_users_and_administrators_guide/sect-managing_confined_services-the_apache_http_server-booleans#sect-Managing_Confined_Services-The_Apache_HTTP_Server-Booleans) <i>docs.redhat.com</i></cite>

[^httpd_can_network_relay]: <q>`httpd` を正引きまたはリバースプロキシーとして使用する場合は、このブール値を有効にします。</q><cite>[13.3. ブール値 | Red Hat Product Documentation](https://docs.redhat.com/ja/documentation/red_hat_enterprise_linux/7/html/selinux_users_and_administrators_guide/sect-managing_confined_services-the_apache_http_server-booleans#sect-Managing_Confined_Services-The_Apache_HTTP_Server-Booleans) <i>docs.redhat.com</i></cite>

### サービスの起動
```bash
systemctl --now start httpd nginx
```