---
title: APIのレスポンスがクリアされているかどうかを判定する方法はありますか？
description: HTTPレスポンスヘッダーの`Age`を活用することで、キャッシュされたレスポンスの経過時間を確認し、データの新鮮度を判定できます。SSGやISRでのキャッシュ最適化に役立ちます。
category: howto
---

import StructuredDataFaqs from '@site/src/components/StructuredDataFaqs';

<StructuredDataFaqs faqs={[
    { question: "APIのレスポンスがクリアされているかどうかを判定する方法はありますか？", answer: "HTTPレスポンスヘッダーの`Age`を活用することで、キャッシュされたレスポンスの経過時間を確認し、データの新鮮度を判定できます。SSGやISRでのキャッシュ最適化に役立ちます。" }
]} />

HTTPレスポンスヘッダーの`Age`を活用することで、キャッシュされたレスポンスの経過時間を確認し、データの新鮮度を判定できます。
この方法は、SSG（Static Site Generation）やISR（Incremental Static Regeneration）でのキャッシュ最適化に特に有効です。

## `Age`ヘッダーとは

`Age`ヘッダーは、HTTPキャッシュ（CDNやリバースプロキシ）がレスポンスをキャッシュしてから経過した時間を秒単位で示す標準的なHTTPヘッダーです。

### 例

```
Age: 120
```

→ レスポンスは120秒前（2分前）にキャッシュに格納されました。

### 特徴

- CDN（Cloudflare、Fastly、Amazon CloudFrontなど）が自動的に付与します
- Kurocoの管理画面でキャッシュ設定を行った場合、`Age`ヘッダーが利用可能です
- `Cache-Control`ヘッダーと組み合わせてキャッシュ戦略を可視化できます

## Next.jsでの取得方法

### 1. サーバーサイド（`getServerSideProps`、API Routes、Route Handlers）

サーバー環境ではCORS制約がないため、単純にヘッダーを取得できます：

```javascript
export async function getServerSideProps() {
  const res = await fetch('https://example.g.kuroco.app/rcms-api/1/content/list');
  const age = res.headers.get('age');
  const data = await res.json();

  console.log(`キャッシュ経過時間: ${age}秒`);

  return {
    props: { data, age: Number(age) || 0 }
  };
}
```

### 2. クライアントサイド（ブラウザ環境）

ブラウザから`Age`ヘッダーを取得するには、Kuroco側でCORS設定が必要です。

#### Kurocoでの設定手順

1. 管理画面より[API] → [セキュリティ] → [CORS]を開く
2. `Access-Control-Expose-Headers`に`Age`を追加

```
Access-Control-Expose-Headers: Age, Content-Type
```

:::tip
すでに他のヘッダーを公開している場合は、カンマ区切りで`Age`を追加してください。
:::

#### フロントエンドでの取得

```javascript
const res = await fetch('https://example.g.kuroco.app/rcms-api/1/content/list');
const age = res.headers.get('age');

if (age) {
  console.log(`キャッシュ経過時間: ${age}秒`);
} else {
  console.log('キャッシュされていないレスポンス、または新しいレスポンス');
}
```

## 活用パターン

### 1. ISR（Incremental Static Regeneration）の再生成判断

Next.jsのISRと組み合わせて、データの鮮度に基づいて再生成の必要性を判断できます。

```javascript title="pages/blog/[slug].js"
export async function getStaticProps({ params }) {
  const res = await fetch(`https://example.g.kuroco.app/rcms-api/1/blog/${params.slug}`);
  const age = Number(res.headers.get('age') || 0);
  const data = await res.json();

  // Ageが5分（300秒）以上経過している場合
  const isStale = age > 300;

  return {
    props: {
      data,
      age,
      isStale
    },
    revalidate: 60, // 通常のISR間隔
  };
}

export default function BlogPost({ data, age, isStale }) {
  return (
    <div>
      <article>{data.content}</article>
      {isStale && (
        <div className="notice">
          このデータは{Math.floor(age / 60)}分前にキャッシュされました
        </div>
      )}
    </div>
  );
}
```

### 2. キャッシュ状態の可視化

開発環境や管理画面で、データの鮮度をモニタリングできます。

```javascript
async function fetchWithCacheInfo(url) {
  const res = await fetch(url);
  const age = Number(res.headers.get('age') || 0);
  const cacheControl = res.headers.get('cache-control');
  const data = await res.json();

  return {
    data,
    cacheInfo: {
      age,
      ageMinutes: Math.floor(age / 60),
      cacheControl,
      isFresh: age < 60, // 1分以内なら新鮮
    }
  };
}

// 使用例
const { data, cacheInfo } = await fetchWithCacheInfo(
  'https://example.g.kuroco.app/rcms-api/1/content/list'
);

console.log('キャッシュ情報:', cacheInfo);
// {
//   age: 245,
//   ageMinutes: 4,
//   cacheControl: "public, max-age=300",
//   isFresh: false
// }
```

### 3. SSG（Static Site Generation）での活用

ビルド時にデータの鮮度を確認し、古いキャッシュを避けることができます。

```javascript title="scripts/prefetch.js"
import fs from 'fs/promises';

async function prefetchData() {
  const res = await fetch('https://example.g.kuroco.app/rcms-api/1/content/list');
  const age = Number(res.headers.get('age') || 0);
  const data = await res.json();

  // キャッシュが古い場合は警告
  if (age > 600) { // 10分以上
    console.warn(`警告: データが古い可能性があります（${Math.floor(age / 60)}分前）`);
  }

  await fs.writeFile(
    './data/prefetched.json',
    JSON.stringify({ data, fetchedAt: new Date(), age }, null, 2)
  );

  console.log('データのプリフェッチ完了');
}

prefetchData();
```

### 4. 条件付きデータ再フェッチ

`Age`ヘッダーに基づいて、不要なAPI呼び出しを削減できます。

```javascript
const CACHE_THRESHOLD = 180; // 3分

async function fetchIfStale(url, cachedData) {
  // まずHEADリクエストでAgeだけをチェック（オプション）
  const headRes = await fetch(url, { method: 'HEAD' });
  const age = Number(headRes.headers.get('age') || 0);

  // キャッシュが新鮮な場合は、既存のデータを使用
  if (age < CACHE_THRESHOLD && cachedData) {
    console.log('キャッシュされたデータを使用');
    return cachedData;
  }

  // キャッシュが古い場合は新しいデータを取得
  console.log('新しいデータを取得');
  const res = await fetch(url);
  return await res.json();
}
```

## メリット

1. **キャッシュの可視化**
   - データがどれだけ前にキャッシュされたかを即座に把握できます

2. **SSR/ISRの最適化**
   - 再生成タイミングを柔軟に制御できます

3. **不要なAPIコールの削減**
   - `Age`に基づいて再フェッチを省略し、API利用料を削減できます

4. **デバッグの効率化**
   - キャッシュ動作の検証が容易になります

## 注意事項

### 1. CDN構成への依存

`Age`ヘッダーは、CDNや中間キャッシュ層で付与されます。Kurocoで適切なキャッシュ設定が行われていない場合、`Age`ヘッダーが存在しない可能性があります。

### 2. CORS設定の確認

ブラウザから`Age`ヘッダーを取得する場合は、必ず`Access-Control-Expose-Headers`に`Age`を追加してください。

### 3. データ生成時刻との違い

`Age`ヘッダーは「キャッシュに格納されてからの時間」を示すため、必ずしも「データがバックエンドで生成された時刻」を示すわけではありません。

### 4. ゼロ値の扱い

`Age: 0`または`Age`ヘッダーが存在しない場合は、キャッシュされていない新しいレスポンスか、キャッシュが無効化されている可能性があります。

```javascript
const age = res.headers.get('age');

if (age === null) {
  console.log('キャッシュされていない、またはAgeヘッダーが提供されていません');
} else if (Number(age) === 0) {
  console.log('新しくキャッシュされたレスポンス');
} else {
  console.log(`キャッシュ経過時間: ${age}秒`);
}
```

## Kurocoでのキャッシュ設定

`Age`ヘッダーを活用するには、Kurocoでキャッシュ設定を適切に行う必要があります。

### APIエンドポイントのキャッシュ設定

1. 管理画面より[API] → [エンドポイント]を開く
2. 対象のエンドポイントを選択
3. [キャッシュ]タブで`Cache-Control`を設定

```
例: public, max-age=300
```

これにより、レスポンスは5分間キャッシュされ、`Age`ヘッダーでキャッシュの経過時間を確認できます。

## まとめ

`Age`ヘッダーを活用することで、KurocoのAPIレスポンスの鮮度を定量的に把握できます。特にSSGやISRを使用している場合、この情報を活用することで：

- **キャッシュ戦略の最適化**
- **不要なAPI呼び出しの削減**
- **データの鮮度を保証**

といったメリットが得られます。Next.jsやNuxt.jsなどのモダンなフレームワークと組み合わせることで、効率的な静的サイト生成とキャッシュ管理が実現できます。

## 関連ドキュメント

- [Nuxt.jsのSSGを使用してAPIコール回数を削減できますか？](/ja/docs/faq/can-i-use-nuxt-js-ssg-to-reduce-api-calls/)
- [KurocoFrontの制約事項](/ja/docs/about/kurocofront/)
