迷惑メールにならないためのエンジニアにできる最大限の対応。php mb_send_mailからSPFとDKIMとDMARC

以下の記事は、　**「迷惑メールにならないためのエンジニアができる最大限の対応」** として、CentOS 7（あるいは Amazon Linux 2 など同等の環境）+ nginx + postfix + PHP + opendkim による **SPF / DKIM / DMARC** 設定方法をまとめたものです。  
（※ 2018年09月22日 に作成した記事を再構成しました）
企業のメール送信はビジネス上の信頼に直結するため、迷惑メール判定されないように最大限の対策を施すことが重要です。  
近年では主要なメールサーバーの受信メールの **95％** が迷惑メールと分類されると言われており、正しい設定を行っていても誤って迷惑メール判定されることがあります。したがって、**本記事で解説するような設定をきちんと行う**ことはもちろん、**メール本文（内容）や送信形態**にも注意する必要があります。

---

# 目次
1. **本記事の狙いと前提環境**
2. **そもそもなぜ迷惑メールになるのか？**
3. **SPF / DKIM / DMARC とは？**
4. **Postfixを使った送信専用サーバー (Null client) の構築**
5. **SPFの設定**
6. **DKIMの設定 (opendkim)**
7. **DMARCの設定**
8. **PHP (mb_send_mail) 側での設定ポイント**
9. **メールヘッダの書き方・注意点 (Return-Path と Envelope From の違い)**
10. **トラブルシューティングとテスト方法**
11. **まとめと追加の参考情報**

---

## 1. 本記事の狙いと前提環境
### 1.1 本記事の狙い
- **迷惑メール判定を回避し、正しくユーザーに届ける**ために、SPF/DKIM/DMARC を中心にサーバーと DNS 側の設定を網羅的に解説します。  
- **エンジニアとして設定可能な最大限の対応**をまとめ、初心者にもわかりやすい形でサーバー構築とメール送信設定のポイントを整理します。

### 1.2 前提環境
- OS: CentOS 7 または Amazon Linux 2 など (今後は後継の AlmaLinux / Rocky Linux への移行も考慮推奨)  
- Web サーバー: nginx  
- MTA (Mail Transfer Agent): postfix  
- PHP 7 以降 (mb_send_mail 関数使用)  
- opendkim (DKIM 署名用)  

本記事の設定例は **CentOS7** 前提で書かれていますが、Amazon Linux 2 や他の RedHat系ディストリビューションでも大筋同じ手順になります。

---

## 2. そもそもなぜ迷惑メールになるのか？ 
迷惑メール判定される原因を大きく分解すると、以下の通りです。

```
迷惑メールになる原因
├─ 1) 認証不備 (SPF, DKIM, DMARC が未設定 / 誤設定)
├─ 2) メールの本文やヘッダ情報
│   ├─ 文中に怪しいキーワードやリンクが多い
│   ├─ 大量送信でスパム認定
│   └─ 文面の品質 (件名や差出人、本文の関連性) が低い
├─ 3) IP やドメインの評判 (レピュテーション) が低い
│   ├─ 過去にスパムを大量送信してブラックリスト化
│   └─ 新規IPやドメインでスコアが低い
└─ 4) 受信者や受信サーバーのローカルルール
    └─ 個別にスパム報告が多い
```

上記のうち、**1) 認証不備** はエンジニアが対策することでコントロールできる要素です。本記事では **SPF・DKIM・DMARC** の正しい導入と、**PHP の送信設定** を中心に解説します。  
また、**2) メール本文** にも注意して、総務省のガイドラインや迷惑メール相談センターが提供する情報を踏まえて文面を工夫しましょう。

---

## 3. SPF / DKIM / DMARC とは？ 
### 3.1 SPF (Sender Policy Framework)
- **What**: 送信元 IP アドレスが、正規のドメインの送信者として認められているかを DNS で検証する仕組み  
- **Why**: なりすまし (偽装) メールを防ぎ、受信サーバーが送信元を信頼できるようにする  
- **How**: ドメインの DNS レコード (TXT or SPFレコード) に、メールを送信してもよい IP アドレスを明記  

### 3.2 DKIM (DomainKeys Identified Mail)
- **What**: 送信メールに電子署名を付与し、受信サーバーが DNS に公開された公開鍵と照合することで改ざんやなりすましを防ぐ技術  
- **Why**: メールヘッダや本文が改ざんされていないことを保証し、送信ドメインの正当性を高める  
- **How**: 送信サーバーに秘密鍵を、DNS に公開鍵を設定。送信時に署名を付与し、受信サーバーが検証  

### 3.3 DMARC (Domain-based Message Authentication, Reporting & Conformance)
- **What**: SPF と DKIM を組み合わせた認証結果を管理するポリシー  
- **Why**: ドメイン管理者が「SPFとDKIMの両方/いずれかが一致しないメールをどう扱うか」を受信サーバーに指示  
- **How**: DNS に DMARC ポリシー (TXT レコード) を設定し、「none / quarantine / reject」などの動作方針を示す  

---

## 4. Postfixを使った送信専用サーバー (Null client) の構築
### 4.1 Null client とは
- 受信はせず「送信専用」としてメールを転送する構成を指します。大規模システムでは、アプリケーションサーバーにメール受信をさせないケースが多く、送信専用サーバーを構築する際に参考になります。  

以下の設定例では **postfix** を Null client として構築しています。詳細は下記リンクも参考にしてください。  
[NullクライアントのPostfix環境の作成 (Qiita)](https://qiita.com/yizumi1012xxx/items/9e3f0f04086003d39674)

---

## 5. SPFの設定
### 5.1 DNS による設定
SPF レコードを DNS に追加します。主に `TXT` レコードとして以下のように定義します (※DNS プロバイダや管理画面によって書式は多少異なる)。

```
example.jp.  IN TXT  "v=spf1 ip4:192.0.2.1 -all"
```

- `ip4:192.0.2.1` には、送信元 Web サーバーやアプリケーションサーバーのグローバル IP を指定してください。  
- `-all` は「この IP 以外からのメール送信はすべて不正とみなす」ことを意味します。  
- 複数の送信元がある場合は `include:～` や `ip4:～` を列挙します。

### 5.2 SPF 設定確認
設定後、**dig** コマンドなどで以下のように確認します。

```
dig txt example.jp
```
`"v=spf1 ip4:192.0.2.1 -all"` のような文字列が返ってきたら OK です。

---

## 6. DKIMの設定 (opendkim)
### 6.1 opendkim のインストール
CentOS7 では EPEL リポジトリに含まれています。

```bash
# (rootユーザで)
yum -y install epel-release
yum -y install opendkim
```

### 6.2 暗号化キー保存用ディレクトリの作成
複数ドメイン運用することを想定し、ドメインごとにディレクトリを用意します。
```bash
mkdir /etc/opendkim/keys/sample.com
```

### 6.3 暗号化キーの作成
`opendkim-genkey` コマンドで鍵ペアを生成します。
```bash
opendkim-genkey -D /etc/opendkim/keys/sample.com/ -d sample.com -s default
```
- `-s` はセレクター名 (ここでは “default”)  
- 生成される `default.private` (秘密鍵) と `default.txt` (公開鍵) が最も重要です。  

### 6.4 ファイルオーナーの変更
```bash
chown -R opendkim:opendkim /etc/opendkim/keys
ls -l /etc/opendkim/keys/sample.com/
# -rw------- 1 opendkim opendkim 887 ... default.private
# -rw------- 1 opendkim opendkim 317 ... default.txt
```
秘密鍵は権限を厳しく管理しましょう。

### 6.5 /etc/opendkim.conf の設定
```bash
vi /etc/opendkim.conf

Mode                sv
KeyTable            refile:/etc/opendkim/KeyTable
SigningTable        refile:/etc/opendkim/SigningTable
ExternalIgnoreList  refile:/etc/opendkim/TrustedHosts
InternalHosts       refile:/etc/opendkim/TrustedHosts
Socket              inet:8891@localhost
```
`KeyFile` はコメントアウト (複数ドメイン運用の場合は KeyTable/SigningTable を参照するため)。

### 6.6 KeyTable の設定
```bash
vi /etc/opendkim/KeyTable

default._domainkey.sample.com sample.com:default:/etc/opendkim/keys/sample.com/default.private
```

### 6.7 TrustedHosts の設定
```bash
vi /etc/opendkim/TrustedHosts

127.0.0.1
::1
smtp.sample.com
```
ローカルホストや自分のサーバードメインを設定。

### 6.8 SigningTable の設定
```bash
vi /etc/opendkim/SigningTable

*@sample.com default._domainkey.sample.com
```

### 6.9 DNS への公開鍵登録
`/etc/opendkim/keys/sample.com/default.txt` の中身を DNS のゾーンファイル (TXT レコード) に設定します。たとえば以下のようになります(改行や括弧の有無はDNS管理によって異なる):

```
default._domainkey IN TXT (
 "v=DKIM1; k=rsa; "
 "p=MIGfMA0GCSqG...." 
 )
```

あわせて DKIM ポリシーも設定例として以下を入れます (任意)。
```
_adsp._domainkey.sample.com IN TXT "dkim=unknown"
```

#### ※ AWS Route 53 への登録上の注意
- Route53 はレコードの 255 文字制限を超える場合、ダブルクォート `" "` で分割して入力できます。  
- 実際に `dig` すると 1レコードとして返ってきます。

### 6.10 postfix 側に opendkim を紐付け
```bash
vi /etc/postfix/main.cf

smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = $smtpd_milters
milter_default_action = accept
```

### 6.11 opendkim の起動/有効化
```bash
systemctl enable opendkim
systemctl start opendkim
systemctl status opendkim
```
### 6.12 postfix の再読み込み
```bash
systemctl reload postfix
# or
postfix reload
```

---

## 7. DMARCの設定
### 7.1 DNS に TXT レコードを追加 (最小設定)
```
_dmarc.sample.com. IN TXT "v=DMARC1; p=none;"
```
- `p=` の値でポリシーを指定（none, quarantine, reject）。  
- より厳格にするなら `"v=DMARC1; p=reject; rua=mailto:postmaster@sample.com;"` などとし、レポート送付先 (rua) も設定できます。  

---

## 8. PHP (mb_send_mail) 側での設定ポイント
### 8.1 PHP の文字エンコード設定
```ini
; /etc/php.ini など
[mbstring]
mbstring.language = Japanese
mbstring.internal_encoding = UTF-8
```
もし ini ファイルの編集が難しい場合、送信前に以下を呼び出す方法もあります。

```php
mb_language("ja");
mb_internal_encoding("UTF-8");
```

### 8.2 mb_send_mail の第5引数
`mb_send_mail()` の第5引数は「**Envelope From (エンベロープ用 Return-Path)**」を指定するために重要です。  
```php
$param = "-ffrom@sample.com"; 
mb_send_mail($to, $subject, $body, $headers, $param);
```
- `-f` に続けて、**スペースなし** で送信元メールアドレスを記述します。  
- ここを指定しないと、OSユーザー(例: `nginx@sample.com`) が Return-Path になり、**メール認証の整合性** が崩れる可能性があります。  

### 8.3 ヘッダの書き方 (Return-Path と From)
```php
$headers = '';
$headers .= "Content-Type: text/plain\r\n"; // HTMLメールの場合 text/html
$headers .= "Return-Path: from@sample.com\r\n";
$headers .= "From: " . mb_encode_mimeheader("差出人名") . " <from@sample.com>\r\n";
$headers .= "Sender: " . mb_encode_mimeheader("差出人名") . " <from@sample.com>\r\n";
$headers .= "Reply-To: from@sample.com\r\n";
$headers .= "Organization: " . mb_encode_mimeheader("組織名") . "\r\n";
$headers .= "Precedence: bulk\r\n"; // 大量送信を明示する場合
$headers .= "X-Sender: from@sample.com\r\n";
$headers .= "X-Priority: 3\r\n"; // 通常の優先度

$param = "-ffrom@sample.com";
mb_send_mail($to, $subject, $body, $headers, $param);
```
- **Return-Path** はヘッダでも指定できますが、実際の Envelope From は第5引数 (`-f` オプション) で決まります。  
- **Precedence: bulk** は大量メールであることを示し、受信サーバーに「一斉送信」である旨を伝えます。  

---

## 9. メールヘッダの書き方・注意点 (Return-Path と Envelope From の違い)
- **メールのエンベロープ (Envelope From)**: 実際に配送されるときに使用される送信元。`MAIL FROM` コマンド等で指定される。  
- **メールヘッダ (From, Return-Path, Sender)**: メール本文(ヘッダ部)に書かれている情報。  
- エンベロープとヘッダの差出人が異なると受信サーバーによってはスパム判定されやすいので、**Envelope From と From を同じにするのが基本**です。

---

## 10. トラブルシューティングとテスト方法
### 10.1 設定反映の確認
1. **SPF**: `dig txt example.com` で正しい `v=spf1 ...` が返るか  
2. **DKIM**: `dig txt default._domainkey.example.com` で公開鍵が返るか  
3. **DMARC**: `dig txt _dmarc.example.com` で設定が返るか  

### 10.2 メール送信テスト
- **mailコマンド**(Linux) や **swaks**、または実際に PHP の `mb_send_mail` で送信し、  
- GMail や Outlook.com の受信メールヘッダを確認して **Authentication-Results** が `spf=pass, dkim=pass, dmarc=pass` となっているかを確認する。  

### 10.3 送信ログと DKIM のログ
- `/var/log/maillog` や `/var/log/secure` にエラーがないか確認。  
- DKIM の署名ミスなどがある場合は、opendkim のログに警告が出ることがあります。

### 10.4 追加の検証サービス
- [mail-tester.com](https://www.mail-tester.com/) にテストメールを送ると、SPF / DKIM / DMARC / スパムスコア の詳細評価を得られます。  
- [MXToolBox](https://mxtoolbox.com/) などで DNS レコードやブラックリストを確認できます。

---

## 11. まとめと追加の参考情報
**主要なメールサーバーは 95％ をスパムと判定している現状**では、残り 5％ に入るためにも **SPF / DKIM / DMARC の設定、Envelope From の整合性、メール本文の最適化** は必須です。  
それでも迷惑メールに分類される場合は、**本文中のキーワードや外部リンク数、送信頻度、受信者からのスパム報告** などが原因の可能性があります。総務省のガイドラインも参考に、**内容面でもスパム誤判定を受けにくい文章を書く**ように気をつけましょう。

- 総務省ガイドライン: [迷惑メール情報](http://www.soumu.go.jp/main_sosiki/joho_tsusin/d_syohi/pdf/m_mail_081114_1.pdf)  
- 迷惑メール相談センター: [メールヘッダ情報の確認方法](https://www.dekyo.or.jp/soudan/contents/ihan/header.html)

### 11.1 今後の発展 (MECE 視点)
- **インフラ面**: 逆引き DNS (PTR レコード) の整合性、IP レピュテーション対策  
- **アプリケーション面**: メール配信ライブラリ (PHPMailer, SwiftMailer など) を使い、細かいパラメータやマルチパート送信を最適化  
- **コンテンツ面**: A/B テストや文面品質、リンク先の安全性・ドメイン統一  
- **運用面**: 送信リストの管理、不要メール送信の抑制、定期的なブラックリストチェック  

---

# 最後に
- **送信ドメイン認証(SPF/DKIM/DMARC) + メール本文の適正化** が、迷惑メール対策の大黒柱です。  
- さらに **受信者・顧客とのコミュニケーション** や **ドメイン/IP の評判管理** といった継続的な運用を行うことで、迷惑メール判定リスクを大幅に低減できます。  
- 本記事の手順を踏まえてサーバー設定と DNS 設定をきちんと行い、**安心して届けたい人に届くメール送信** を実現しましょう。

これが、**迷惑メールにならないためのエンジニアとしてできる最大限の対応** です。ご参考になれば幸いです。