既存プロジェクトとデザインシステムを統一するため、内部開発のアプリのフォントをInterからNoto Sans JPに変更する必要がありました。

最初はGoogle Fonts CDN方式を使用していましたが、実装を進める中で2つの課題が浮かび上がりました。

1つ目は、**CSP(Content Security Policy)の設定が複雑化**していたこと。外部フォントサービスのドメインを許可リストに追加する必要があり、セキュリティ設定の管理が煩雑でした。

2つ目は、**外部サービスへの依存**です。Google Fontsがダウンのときにフォントが読み込めないリスクや、通信遅延がページロード時間に影響を与える懸念がありました。

そこで、Next.jsのnext/fontによるセルフホスティング方式に移行しました。実装してみると、これは想像以上にシンプルで、パフォーマンスとセキュリティの両面で大きな改善が実現できました。

## こんな人におすすめ

この記事は以下のような方に役立ちます。

- Next.js で日本語フォント(Noto Sans JP)をセットアップしたい方
- Google Fonts CDN への外部依存を減らしたい方
- Tailwind CSS v4 を使用していて、フォント設定を統合したい方
- CSP(Content Security Policy)を厳しく設定している方
- フォント周りのパフォーマンス最適化に関心のある方

## 要件

- InterフォントをNoto Sans JPに置き換え
- Google Fonts CDNへの外部リクエストを削除
- Tailwind CSS v4の@themeブロックとの統合
- CSP(Content Security Policy)の最適化

## 実装方針

1. next/font/googleからNoto Sans JPをインポート
2. フォント設定オブジェクトを作成(weights, display, variable)
3. <html>要素にCSS変数を適用
4. <body>要素にfont-sansクラスを追加
5. globals.cssからCDN importを削除し、@themeブロックを更新
6. next.config.tsのCSPから外部フォントドメインを削除

## ポイント

### next/fontの設定

``tsx
import { Noto_Sans_JP } from 'next/font/google';

const notoSansJP = Noto_Sans_JP({
subsets: ['latin'], // 最小限のプリロード(日本語は自動で含まれる)
weight: ['400', '500', '600', '700'],
display: 'swap', // FOUT防止
variable: '--font-noto-sans-jp',
});

// htmlにCSS変数を適用
<html lang="ja" className={notoSansJP.variable}>
`

### Tailwind CSS v4との統合

`css
@theme {
--font-sans: var(--font-noto-sans-jp), -apple-system, BlinkMacSystemFont,
'Segoe UI', Roboto, sans-serif;
}
`

### CSPの簡素化

セルフホスティングにより、以下のドメイン許可を削除できた:
-
https://fonts.googleapis.com(style-src)
-
https://fonts.gstatic.com(font-src)

## 学び

-
next/fontはビルド時にフォントをダウンロードし、自動的に最適化・セルフホスティングする
-
subsets: ['latin']は最小限のプリロード設定。日本語文字はnext/fontが自動的に処理する
- CSS変数を使うことで、Tailwind CSSのユーティリティクラスとシームレスに統合できる
- 外部フォントサービスへの依存を削除することで、CSPがシンプルになりセキュリティが向上する

## 得られた効果

この実装により、以下の改善が実現できました。

**即時の効果:**

- **CSP ルールの削減**: Google Fonts に関連する 2 つのドメイン許可を削除できました
-
https://fonts.googleapis.com(style-src)
-
https://fonts.gstatic.com(font-src)
- **セキュリティの強化**: 外部サービスへの依存がなくなり、セキュリティポリシーがシンプルになりました

**パフォーマンス面での改善:**

- **First Contentful Paint(FCP)の改善**: フォントがビルド時にダウンロードされるため、ページロード時の外部通信が削減されました
- **安定性の向上**: Google Fonts サービスの可用性に依存しなくなり、確実にフォントが読み込まれるようになりました

**開発効率の向上:**

- セルフホスティングにより、フォント設定が一箇所に集約されて管理が楽になりました
- Tailwind CSS との統合がシームレスで、デザインシステムの保守性が向上しました

## トラブルシューティング

### フォントが正しく読み込まれない

**現象:** ビルド後にフォントが適用されず、フォールバックフォントが表示される

**原因と解決策:**

1. **subsetsの指定ミス**
`javascript
// ❌ 誤った指定
subsets: ['japanese'] // この指定は不要

// ✅ 正しい指定
subsets: ['latin'] // 日本語は自動で含まれる
`

2. **環境変数の未設定**
`bash
# 環境変数が正しく設定されているか確認
echo $NODE_ENV # production であることを確認

# 開発環境ではnext devで確認
npm run dev
`

3. **キャッシュの問題**
`bash
# Next.jsキャッシュをクリア
rm -rf .next
npm run build
`

### CSS変数が適用されない

**現象:** Tailwind CSSのfont-sansが適用されない

**解決策:**

1. **html要素のクラス確認**
`html
<!-- html要素にfontクラスが適用されているか確認 -->
<html lang="ja" class="font-sans">
`

2. **globals.cssの@themeブロック確認**
`css
@theme {
--font-sans: var(--font-noto-sans-jp), -apple-system, BlinkMacSystemFont,
'Segoe UI', Roboto, sans-serif;
}
`

### ビルドサイズが大きくなる

**現象:** フォントファイルのためにビルドサイズが増加する

**解決策:**

1. **必要なweightのみを指定**
`javascript
const notoSansJP = Noto_Sans_JP({
weight: ['400', '500', '700'], // 実際に使用するweightのみ
// ...
});
`

2. **preloadの無効化(必要に応じて)**
`javascript
const notoSansJP = Noto_Sans_JP({
display: 'swap',
variable: '--font-noto-sans-jp',
preload: false, // 明示的にpreloadを無効化
});
`

### FOUT(Flash of Unstyled Text)が発生する

**現象:** フォント読み込み中に一時的にフォールバックフォントが表示される

**解決策:**

1. **displayオプションの確認**
`javascript
const notoSansJP = Noto_Sans_JP({
display: 'swap', // FOUTを許容して読み込み速度を優先
// display: 'block' // 完全な読み込みを待つ場合(遅延が発生)
});
`

2. **font-display CSSの確認**
カスタムCSSでフォントの表示動作を調整:
`css
@font-face {
font-family: 'Noto Sans JP';
font-display: swap;
}
`

## よくある質問(FAQ)

### Q1. Google Fontsとセルフホスティング、どちらを選ぶべきですか?

**A:** プロジェクトの要件に応じて選択してください:

**Google Fontsを選ぶべきケース:**
- プロトタイプや開発初期段階
- 多言語対応が必要でフォントファイルサイズが大きい場合
- CDN経由でフォントを配信する利便性を重視する場合

**セルフホスティングを選ぶべきケース:**
- セキュリティポリシー(CSP)を厳しく設定している場合
- 外部サービスへの依存を排除したい場合
- パフォーマンス最適化を重視する場合
- オフライン環境でのフォント表示が必要な場合

### Q2. サブセット(subsets)の指定は必要ですか?

**A:** 基本的には不要です。
subsets: ['latin'] を指定するだけで、next/font が自動的に日本語文字を含めてくれます。日本語のみのプロジェクトでも latin サブセットを指定することがベストプラクティスです。

### Q3. フォントファイルのサイズはどのくらいになりますか?

**A:** フォントのweight(太さ)の数によって異なりますが、一般的に:
- **1つのweight**: 約 200-400KB
- **4つのweight**(400, 500, 600, 700): 約 1-1.5MB
- **すべてのweight**: 約 3-4MB

実際のサイズは
npm run build 後の .next フォルダ内で確認できます。

### Q4. セルフホスティングすると、本当にパフォーマンスが向上しますか?

**A:** はい、以下の理由でパフォーマンスが向上します:
- **First Contentful Paint (FCP) の改善**: フォントがビルド時にバンドルされるため、ページロード時の外部リクエストが削減されます
- **TTFBの改善**: 外部CDNへの接続時間がなくなるため
- **キャッシュ効率**: ユーザーがサイトを再訪問した際、ブラウザキャッシュからフォントが読み込まれます

ただし、初回ロード時のファイルサイズが増加するため、キャッシュ戦略が重要になります。

### Q5. 複数のフォントを同時にセルフホスティングできますか?

**A:** はい、複数のフォントを同時に使用できます。例えば:

`typescript
import { Noto_Sans_JP } from 'next/font/google';
import { Noto_Serif_JP } from 'next/font/google';

const notoSansJP = Noto_Sans_JP({ ... });
const notoSerifJP = Noto_Serif_JP({ ... });

// Tailwind CSSでそれぞれのフォントを定義
@theme {
--font-sans: var(--font-noto-sans-jp), sans-serif;
--font-serif: var(--font-noto-serif-jp), serif;
}
`

### Q6. next/fontはNext.js 13以降でも使用できますか?

**A:** はい、Next.js 13以降(App Router)でも使用できます。設定方法は基本的に同じですが、フォントファイルの最適化がさらに強化されています。

### Q7. フォントのライセンスはどうなっていますか?

**A:** Noto FontsはApache License 2.0で提供されており、商用利用も可能です。詳細は[Google FontsのNoto Sans JPページ](https://fonts.google.com/noto/specimen/Noto+Sans+JP)で確認してください。

### Q8. セルフホスティングのデメリットはありますか?

**A:** 主なデメリットは以下の通りです:
- **ビルドサイズの増加**: フォントファイルがバンドルされる
- **バージョン管理**: フォントの更新時に手動で対応が必要
- **CDNとのトレードオフ**: キャッシュ戦略を考慮する必要がある

## まとめ

Next.jsの
next/fontを使ったNoto Sans JPのセルフホスティングは、シンプルな設定でパフォーマンスとセキュリティの両面で大きなメリットをもたらします。

**実装のポイント:**
1.
subsets: ['latin']で最小限のプリロード
2. CSS変数を使った柔軟なフォント管理
3. Tailwind CSS v4の
@theme`ブロックとの統合
4. CSPの簡素化によるセキュリティ向上

**得られる効果:**
- パフォーマンスの向上(FCP改善、外部通信削減)
- セキュリティの強化(外部依存の排除)
- 開発効率の向上(設定の一元化)

この手法は、日本語フォントを使用するNext.jsプロジェクトであればほぼそのまま適用できる汎用的なソリューションです。プロジェクトの要件に合わせて、ぜひ試してみてください。

## 参考リンク

- [Next.js Font Optimization Documentation](https://nextjs.org/docs/app/building-your-application/optimizing/fonts)
- [Google Fonts Noto Sans JP](https://fonts.google.com/noto/specimen/Noto+Sans+JP)
- [Tailwind CSS v4 Documentation](https://tailwindcss.com/docs/v4)
- [Web Font Loading Best Practices](https://web.dev/font-loading/)