---
title: "Astroの動的ルーティングでスラッシュを含むタグに対応した記録 | C#もぐもぐ"
description: "Astroのタグ別記事一覧ページで「CI/CD」のようにスラッシュを含むタグを使うとビルドが失敗する問題に遭遇し、キャッチオールルートで解決した記録です。"
url: "https://blog.223n.tech/posts/236/"
---

# Astroの動的ルーティングでスラッシュを含むタグに対応した記録

2026年4月13日

[Astro](https://blog.223n.tech/tags/Astro/) [TypeScript](https://blog.223n.tech/tags/TypeScript/) [Program](https://blog.223n.tech/tags/Program/)

## はじめに

このブログはAstro 5で構築しており、記事にはタグを付けて分類しています。

タグ別の記事一覧ページは`src/pages/tags/[tag].astro`という動的ルーティングで実装していましたが、
「`CI/CD`」のようにスラッシュを含むタグを追加したところ、ビルドが失敗しました。

この記事では、問題の原因と、キャッチオールルートを使った解決方法を記録します。

## 発生した問題

[ChocolateyパッケージのCI/CD移行記事](https://blog.223n.tech/posts/235/)を追加した際、タグに「`CI/CD`」を指定しました。

```yaml
---
title: "ChocolateyパッケージのCI/CDをAppVeyorからGitHub Actionsに移行した記録"
tags: ["GitHub Actions", "Chocolatey", "PowerShell", "CI/CD", "Program"]
---
```

この状態で`pnpm build`を実行すると、次のエラーが発生しました。

```
/tags/CI/CD/index.html  Missing parameter: tag
  Stack trace:
    at getParameter (file:///...astro/dist/core/routing/manifest/generator.js:17:13)
```

## 原因

原因は、Astroの動的ルーティング`[tag].astro`がスラッシュを含むパラメーターに対応していないことです。

`getStaticPaths()`でタグ「CI/CD」を返すと、Astroは`/tags/CI/CD/index.html`を生成しようとします。

しかし、`[tag]`は単一のパスセグメントにしかマッチしないため、`CI/CD`のスラッシュがパス区切りとして解釈されてしまいます。

結果として、`/tags/CI/`の`CD/`部分が`tag`パラメーターとして認識できず、`Missing parameter: tag`エラーになります。

```
# Astroの解釈
/tags/[tag]  →  /tags/CI/CD  →  NG（[tag] = "CI" で "CD" が余る）

# 期待する動作
/tags/[...tag]  →  /tags/CI/CD  →  OK（[...tag] = "CI/CD"）
```

## 解決方法

`[tag].astro`をキャッチオールルート`[...tag].astro`にリネームしました。

キャッチオールルート（rest parameters）は、
スラッシュを含むパスセグメント全体を1つのパラメーターとしてキャプチャします。

### 変更内容

変更はファイル名のリネームのみです。

ロジックの変更は不要でした。

```
# Before
src/pages/tags/[tag].astro

# After
src/pages/tags/[...tag].astro
```

`getStaticPaths()`の実装はそのままです。

```typescript
export async function getStaticPaths() {
  const allPosts = await getCollection("posts");
  const allTags = new Set<string>();

  allPosts
    .filter((post) => !post.data.draft && post.data.tags)
    .forEach((post) => {
      post.data.tags?.forEach((tag: string) => allTags.add(tag));
    });

  return Array.from(allTags).map((tag) => ({
    params: { tag },
    props: { tag },
  }));
}
```

タグ「CI/CD」の場合、`params: { tag: "CI/CD" }`がそのまま`/tags/CI/CD/index.html`を生成します。

### タグリンク

テンプレート内のタグリンクも変更不要です。

```astro
<!-- タグ一覧ページ -->
<a href={`/tags/${tag}`}>

<!-- 記事詳細ページ -->
<a href={`/tags/${tag}`} class="badge bg-primary">
```

`tag`が「CI/CD」の場合、`/tags/CI/CD`というリンクが生成され、キャッチオールルートに正しくマッチします。

## 動的ルーティングの使い分け

Astroの動的ルーティングには2種類あります。

| ルーティング | ファイル名 | マッチ対象 | 用途 |
| - | - | - | - |
| 動的ルート | `[param].astro` | 単一パスセグメント | スラッシュを含まないパラメーター |
| キャッチオールルート | `[...param].astro` | 複数パスセグメント | スラッシュを含む可能性のあるパラメーター |

今回のケースでは、タグ名にスラッシュが含まれる可能性があるため、キャッチオールルートが適切です。

なお、キャッチオールルートは単一セグメントのパラメーターにもマッチするため、
既存のスラッシュを含まないタグ（「Program」「Astro」など）もそのまま動作します。

## 影響範囲の確認

今回の変更で影響を受けるのは、タグ別記事一覧ページのルーティングのみです。

次の箇所は変更不要でした。

- `src/pages/tags/index.astro`（タグ一覧）: リンク生成のみなので影響なし
- `src/pages/posts/[...slug].astro`（記事詳細）: タグへのリンクは`/tags/${tag}`で変更不要
- `src/pages/posts/index.astro`（記事一覧）: 同上
- `src/pages/index.astro`（トップページ）: 同上

## おわりに

Astroの動的ルーティングで、スラッシュを含むタグ名に対応しました。

変更はファイル名を`[tag].astro`から`[...tag].astro`にリネームするだけで、
ロジックやテンプレートの修正は不要でした。

タグ名や、スラッシュを含む可能性のあるパラメーターを扱う場合は、
最初からキャッチオールルートを使っておくと、このような問題を未然に防げます。

## 参考サイト

- [Astro 動的ルーティング](https://docs.astro.build/ja/guides/routing/#%E5%8B%95%E7%9A%84%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0)
- [Astro Restパラメーター](https://docs.astro.build/ja/guides/routing/#rest%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%83%BC)
- [Astro getStaticPaths](https://docs.astro.build/ja/reference/routing-reference/#getstaticpaths)

[記事一覧に戻る](https://blog.223n.tech/posts/)
