フィットネス・健康

スポーツ施設のWebサイトのSEO。テニス・フットサル・ゴルフ練習場など、種目とスクール・貸し出しが混ざる施設のページの分け方と、ビジネスプロフィールのカテゴリと部門、構造化データの型の決め方

テニスコート・フットサル・ゴルフ練習場など、種目とスクール・貸し出しが混ざるスポーツ施設のサイトを、検索に正しく伝える作り方です。ページの3段の組み方、ビジネスプロフィールのカテゴリと部門、構造化データの型の確かめ方まで。

この記事の結論:種目とスクール・貸し出しが混ざるスポーツ施設のサイトは、「施設」「種目」「使い方(スクール・貸し出し・個人利用)」の3段でページを分け、料金・空き・対象を文字で書きます。ビジネスプロフィールのカテゴリは「事業が何であるか」で少なく選び、部門は独立した窓口があるときだけ分けます。構造化データは施設全体の型と部門で書き、型の名前をschema.orgと照らしてから載せます。

この記事の内容のご相談は株式会社bundlyzeへbundlyzeのHP・LP制作について見てみる

この記事は、テニスコート、フットサルコート、ゴルフ練習場、プール、体育館の貸し出しなど、いくつかの種目や使い方を1つの施設で受けている民間のスポーツ施設の経営者・担当者と、そのサイトを作る制作の方に向けて書いています。パーソナルジムのように「店舗・トレーナー・料金・コース」で組める業態は、パーソナルジムのWebサイトのSEOの記事で扱いました。ここでは、種目と使い方が掛け合わさって、ページの分け方とGoogleへの伝え方に迷いやすいところに絞ります。ビジネスプロフィールの基本の設定はジムのGoogleビジネスプロフィールの記事を、時間割の載せ方はスタジオのWebサイトに時間割を載せる記事をご覧ください。

スポーツ施設のサイトは「施設」「種目」「使い方」の3段で組む

種目と使い方が混ざる施設は、トップページに全部を並べると、どの種目の情報もトップページの一部にしかなりません。「テニスコートを2時間借りたい」「子どものサッカースクールを探している」のように、探す人が知りたいことは種目と使い方の組み合わせで変わるので、その組み合わせごとに答えのあるページを用意します。

段 ページの例 書くこと
1. 施設 トップ、アクセス、営業時間、施設の案内 施設名・住所・電話・営業時間・駐車場、全体の設備、種目の一覧へのリンク
2. 種目 /tennis/、/futsal/、/golf/ その種目の設備(面数・打席数・床やコートの種類・照明)、受け方の一覧、種目の料金表へのリンク
3. 使い方 /tennis/school/、/tennis/rental/、/futsal/rental/ 対象・料金・時間割や空き・予約の方法・取り消しの決まり

使い方は、たとえば「スクール(レッスン)」「貸し出し(コート・打席・レーンなどの時間貸し)」「個人利用(打ちっぱなし、プールの個人利用など)」「大会・イベント」などに分けられます。使い方によって、探す人が比べる情報は違います。

使い方 探す人が比べる情報 ページに必ず書くこと
スクール 対象の年齢とレベル、曜日と時間、振替、コーチ クラスごとの対象・曜日・月謝、体験の申込方法
貸し出し 空き、料金(時間帯・曜日)、面数、取り消しの期限 料金表、予約の方法と受付開始、取り消しの決まり
個人利用 料金、混み具合、道具の貸し出し 料金、道具の貸し出しの有無と料金
大会・イベント 日程、参加費、申込の締め切り 開催日、対象、申込方法

種目のページを1種目に1ページにし、使い方のページをその下に置くと、サイトの中の階層がそのまま「どの種目の・どの使い方か」になります。URLも同じ形にそろえておくと、後から種目や使い方を足すときに迷いません。

種目と使い方のページには、料金・空き・対象を文字で書く

料金表を画像だけで載せたり、空き状況を外部の予約システムの画面だけに任せたりすると、そのページを読んだ人にも検索にも、料金と受け方が伝わりにくくなります。料金は時間帯・曜日・会員かどうかの区分ごとに表で書き、予約システムへのリンクの前に「何を・いつから・どう予約できるか」を文章で書きます。料金ページの書き方は、パーソナルジム・整体院の料金ページの記事にまとめています。

スクールのページは、クラスごとに対象(年齢・レベル)、曜日と時間、定員、月謝、振替の決まりを1か所にまとめます。同じ種目でも、大人のレッスンと子どものスクールでは探す人も比べる情報も違うので、ページを分けるか、少なくとも見出しで分けます。

貸し出しのページは、受付の開始(何日前から予約できるか)と、取り消しの期限・料金を、予約の画面に入る前に読めるようにします。屋外のコートなら、雨天の扱い(使えない日の振替や返金)もページに書いておきます。

ビジネスプロフィールは、カテゴリ・部門・名前・営業時間の4つを先に決める

Googleマップに出るビジネスプロフィールは、サイトとは別にGoogleが決めたガイドラインで書き方が決まっています。種目と使い方が多い施設ほど、カテゴリを増やしたり、種目ごとにプロフィールを作ったりしたくなりますが、ガイドラインはそれぞれに条件を付けています。

ポイント1. カテゴリは「事業が何であるか」で、少なく選ぶ

Googleのビジネスプロフィールのガイドラインは、カテゴリ一覧の中から、中心となる事業内容を示すカテゴリのみを可能な限り少なく設定する、としています。カテゴリは「事業に何が含まれるか」ではなく「事業が何であるか」という観点で選び、提供しているサービスや保有している設備を示すことが目的ではない、とも書かれています。ガイドラインの例では、プール付きのモーテルは「Motel」が正しく「水泳プール」は間違い、フィットネスクラブのチェーンは「ヘルスクラブ」が正しく「スポーツジム」「水泳プール」は間違ったカテゴリとされています。

施設が「何であるか」を表す具体的なカテゴリを選び、種目の広がりはサイトの種目のページで伝えます。

ポイント2. 部門のプロフィールは、独立した窓口があるときだけ作る

同じガイドラインは、ビジネス・大学・病院などの中の部門が、個別のビジネスプロフィールを持てる場合を説明しています。対外的に独立した組織として活動する部門は個別のページを作り、名前は母体とも別の部門とも区別します。こうした部門は通常、個別の顧客窓口があり、カテゴリも異なり、営業時間が母体と違う場合もある、とされています。一方で、売り場の一角のようなコーナーは、個別のプロフィールを作れない例に挙げられています。

施設に当てはめると、受付も電話も営業時間も別で運営しているゴルフ練習場やスクールなら部門として分けられる場合があり、同じ受付で扱う「テニスの大人のレッスン」のようなコースは分けられない、と考えるのが自然です。分けるかどうかは、窓口・営業時間・カテゴリが本当に別かで判断し、迷うときは分けずに1つのプロフィールで始めます。

ポイント3. ビジネス名に、種目・地名・サービスを足さない

ガイドラインは、ビジネス名には店舗やウェブサイトで一貫して使い、顧客に認知されている実際の名前を使い、不要な情報を含めるとプロフィールが停止される場合がある、としています。含めてはいけない例として、サービスや商品の情報、所在地の情報(「〇〇インターすぐ」「駅前の」など)、営業時間の情報が挙げられています。「〇〇スポーツパーク テニス・フットサル・ゴルフ」のように種目を名前に足すのはやめ、看板どおりの名前にします。

ポイント4. 営業時間は、レッスンだけで決まる場合は設定しない

ガイドラインは、レッスンなどその日の活動で予定が決まるビジネスや、完全予約制のビジネスでは、営業時間を指定しないよう求めています。営業時間を指定すべきでない例には、学校が挙がっています。施設として決まった開館時間があるならその時間を書き、部門のプロフィールを分けた場合は、部門の営業時間をそれぞれのプロフィールに書きます。スクールの時間割は、プロフィールの営業時間ではなく、サイトのスクールのページで伝えます。

構造化データは、施設全体の型と部門で書き、型の名前を確かめてから載せる

Googleの店舗情報(LocalBusiness)の構造化データの説明は、LocalBusinessのできるだけ具体的な下位の型を使うよう求めていて、必須のプロパティは name と address の2つです。部門を表す department には、部門の名前を原則として「{店舗名} {部門名}」の形で書き、部門名がブランドとして認知されている場合は部門名を単独で書く、と説明しています。

スポーツ施設に使える型を確かめるため、schema.orgが公開している最新の語彙のファイルを取得し、SportsActivityLocation の下位の型を一覧にしました(2026年10月7日、Node.js 22.23.2)。

schema-types.mjsjs
// schema-types.mjs
// schema.org の最新の語彙を取得し、SportsActivityLocation の下位の型を表示する
const URL = 'https://schema.org/version/latest/schemaorg-current-https.jsonld';
const res = await fetch(URL);
const { '@graph': graph } = await res.json();
const types = graph.filter((n) => [].concat(n['@type']).includes('rdfs:Class'));
const parentsOf = (id) => [].concat(types.find((t) => t['@id'] === id)?.['rdfs:subClassOf'] ?? []).map((p) => p['@id']);
const label = (id) => id.replace('schema:', '');
const chain = (id) => { const out = [id]; let p = parentsOf(id); while (p.length) { out.push(p[0]); p = parentsOf(p[0]); } return out.map(label).join(' < '); };

console.log('取得日時:', new Date().toISOString(), 'HTTP', res.status);
console.log(chain('schema:SportsActivityLocation'));
const children = types.filter((t) => parentsOf(t['@id']).includes('schema:SportsActivityLocation')).map((t) => label(t['@id'])).sort();
console.log('下位の型:', children.join(', '));
const dep = graph.find((n) => n['@id'] === 'schema:department');
console.log('department の domain:', [].concat(dep['schema:domainIncludes']).map((d) => label(d['@id'])).join(', '),
  '/ range:', [].concat(dep['schema:rangeIncludes']).map((d) => label(d['@id'])).join(', '));
$ node schema-types.mjs
取得日時: 2026-10-07T01:09:15.475Z HTTP 200
SportsActivityLocation < LocalBusiness < Organization < Thing
下位の型: BowlingAlley, ExerciseGym, GolfCourse, HealthClub, PublicSwimmingPool, SkiResort, SportsClub, StadiumOrArena, TennisComplex
department の domain: Organization / range: Organization

(実際に動かしたスクリプトは、いくつかの型の説明文も表示していましたが、その部分のコードと出力は省いています。また LocalBusiness の親は Organization と Place の2つで、上の表示は最初の親だけをたどっています。)テニスには TennisComplex、ゴルフには GolfCourse がありますが、フットサルや打ちっぱなしのゴルフ練習場を表す型はありません。合う型がない種目は、SportsActivityLocation のままにします。GolfCourse はゴルフ場(コース)を表す型なので、打席だけの練習場に使うかは、ページの内容と合うかで決めます。

次に、施設の台帳(1か所)から、施設全体と種目ごとの部門の JSON-LD を作り、公開前に3つの点を確かめるスクリプトを書きました。台帳の3つ目の部門には、わざと schema.org にない型の名前(DrivingRange)を入れています。施設名・住所・電話はすべて例です。

facility-jsonld.mjsjs
// facility-jsonld.mjs
// 施設の台帳(1か所)から、施設全体と種目ごとの部門の JSON-LD を作り、公開前に3つの点を確かめる
// 1) @type が schema.org に実在する型か  2) name と address があるか(Google の必須項目)
// 3) 部門の name が「施設名 部門名」の形か(Google のガイドの原則の書き方)
import { readFileSync } from 'node:fs';

const facility = JSON.parse(readFileSync(process.argv[2], 'utf8'));
const vocab = await (await fetch('https://schema.org/version/latest/schemaorg-current-https.jsonld')).json();
const known = new Set(vocab['@graph'].filter((n) => [].concat(n['@type']).includes('rdfs:Class')).map((n) => n['@id'].replace('schema:', '')));

const address = {
  '@type': 'PostalAddress', postalCode: facility.postalCode, addressRegion: facility.region,
  addressLocality: facility.locality, streetAddress: facility.street, addressCountry: 'JP',
};
const jsonld = {
  '@context': 'https://schema.org',
  '@type': facility.type,
  '@id': `${facility.url}#facility`,
  name: facility.name,
  url: facility.url,
  telephone: facility.telephone,
  address,
  department: facility.departments.map((d) => ({
    '@type': d.type,
    name: `${facility.name} ${d.name}`,
    url: `${facility.url}${d.path}`,
    telephone: d.telephone ?? facility.telephone,
    address,
  })),
};

const problems = [];
const check = (node, where) => {
  if (!known.has(node['@type'])) problems.push(`${where}: @type「${node['@type']}」は schema.org にない`);
  if (!node.name) problems.push(`${where}: name がない`);
  if (!node.address?.streetAddress) problems.push(`${where}: address がない`);
};
check(jsonld, '施設');
jsonld.department.forEach((d, i) => {
  check(d, `部門${i + 1}(${d.name})`);
  if (!d.name.startsWith(`${facility.name} `)) problems.push(`部門${i + 1}: name が「施設名 部門名」の形でない`);
});
console.log(JSON.stringify(jsonld, null, 2));
console.log(problems.length ? `確かめること ${problems.length}件:\n- ${problems.join('\n- ')}` : '確かめること 0件');

台帳(facility.json)は次のとおりです。

json
{
  "name": "みなとスポーツパーク",
  "type": "SportsActivityLocation",
  "url": "https://example.com/",
  "telephone": "+81-78-000-0000",
  "postalCode": "650-0000",
  "region": "兵庫県",
  "locality": "神戸市中央区",
  "street": "(例)港町1-2-3",
  "departments": [
    { "type": "TennisComplex", "name": "テニスコート", "path": "tennis/" },
    { "type": "SportsActivityLocation", "name": "フットサルコート", "path": "futsal/" },
    { "type": "DrivingRange", "name": "ゴルフ練習場", "path": "golf/" }
  ]
}

出力の JSON-LD は長いので、部門の1つ目と、最後の確認の結果だけを載せます。

$ node facility-jsonld.mjs facility.json
{
  "@context": "https://schema.org",
  "@type": "SportsActivityLocation",
  "@id": "https://example.com/#facility",
  "name": "みなとスポーツパーク",
  …
  "department": [
    {
      "@type": "TennisComplex",
      "name": "みなとスポーツパーク テニスコート",
      "url": "https://example.com/tennis/",
      …
確かめること 1件:
- 部門3(みなとスポーツパーク ゴルフ練習場): @type「DrivingRange」は schema.org にない

存在しない型の名前は、JSON としては正しいので、手で書いていると気づきにくい誤りです。公開の前に、こうした確認をビルドの手順に入れておくと、種目を足したときの書き間違いも拾えます。構造化データの検証の手順は、構造化データの実装と確認の記事にまとめています。この記事では、作った JSON-LD をGoogleのリッチリザルトテストには通していません。

部門をJSON-LDに書くのは、その部門の情報がページに載っているときだけにします。Googleの構造化データの一般的なガイドラインは、ページの読者に表示されないコンテンツをマークアップしないこと、構造化データがページのコンテンツを正確に表すことを求めています。

やってはいけないこと

検索やマップで目立たせようとして、Googleの方針に反する作りにしてしまわないよう、避けたい3つの例を根拠と一緒に挙げます。

例1. 種目×地名のページを量産する

「〇〇市 テニスコート」「△△駅 テニスコート」のように、地名だけを入れ替えた種目のページを並べるのは避けます。Googleのスパムポリシーは、特定の地域や都市を対象にしたページを複数持ってユーザーを1つのページに誘導することや、階層が明確でなく検索結果の一覧に近い、内容が似た複数のページを作ることを、誘導ページの不正使用の例に挙げています。施設は1か所なので、種目のページは1種目に1ページにし、近くの駅や地名はアクセスの案内として書きます。

例2. 設備を並べるためにカテゴリを増やす

プールやジムのエリアがあっても、それが事業の中心でなければ、ビジネスプロフィールのカテゴリには入れません。前の節のとおり、ガイドラインはカテゴリを「事業が何であるか」で少なく選ぶよう求めています。

例3. ビジネス名に種目や「駅前」を入れる

ビジネス名にサービスや所在地の情報を足すと、ガイドラインではプロフィールの停止につながる場合があるとされています。看板どおりの名前にします。

公開した後は、種目ごとの検索語と着地ページを確かめる

公開した後は、Search Consoleの検索パフォーマンスで、ページごとに絞り込み、種目のページにその種目の語で表示が出ているかを見ます。テニスの語でテニスのページではなくトップページが表示されているなら、テニスのページの題・見出し・本文に、料金や受け方が足りていない可能性があるので、まずそのページを見直します。長い問いかけの検索語を見出しに戻す手順は、Search Consoleの長い検索語を見出しに戻す記事にまとめています。

公開前に確かめること

サイトとビジネスプロフィールを公開・更新する前に、次の項目を施設の担当者と制作の担当者で確かめます。

  • 種目ごとに1ページあり、その下に使い方(スクール・貸し出し・個人利用)のページがある
  • 料金は画像だけでなく文字の表で書き、時間帯・曜日・会員の区分が分かる
  • 予約の方法・受付の開始・取り消しの期限・雨天の扱いを、予約の画面に入る前に読める
  • スクールはクラスごとに対象・曜日・定員・月謝・振替が書いてある
  • 地名だけを入れ替えたページがない
  • ビジネスプロフィールのカテゴリは「事業が何であるか」で選び、設備のカテゴリを足していない
  • 部門のプロフィールは、窓口・営業時間・カテゴリが別のものだけ
  • ビジネス名は看板どおりで、種目・地名・サービスを足していない
  • 構造化データの型の名前を schema.org の語彙と照らし、部門の名前は「施設名 部門名」になっている
  • 構造化データに書いた部門・住所・電話が、ページの本文にも載っている

自社でやる場合と、任せる場合

自社で進める場合は、種目ごとの設備や受け方、料金の区分を書き出せる施設の担当者と、ページを公開・更新できる人が要ります。スクールのクラスや料金が変わるたびに直す必要があるので、誰がどのページを直すかを決めておきます。構造化データを入れるなら、HTMLとJSONを編集できる人と、公開前に型や必須項目を確かめる手順が必要です。ビジネスプロフィールのカテゴリや部門を変えるときは、ガイドラインを読み直してから行います。

任せる場合、株式会社bundlyzeでは、ホームページとLPの制作をお受けしています。種目と使い方に合わせたページの組み立てから、料金や受け方の見せ方、スクールの体験の申込の入口まで、一緒に設計します。AI検索に紹介されるための取り組み(LLMO)は、株式会社bundlyzeが自社のサイトでも実践しているものです。大阪・兵庫を中心とした関西のほか、遠方のご相談もオンラインでお受けしています。施設のサイトの作り直しや、公開後の検索語を見ての見直しを一緒に進めたい場合は、ホームページ制作のご案内をご覧ください。

出典(一次情報)

2026年10月7日に閲覧しました。

よくある質問

テニスもフットサルもある施設は、ビジネスプロフィールのカテゴリを種目の数だけ付ければよいですか?

付けません。Googleのビジネスプロフィールのガイドラインは、カテゴリは中心となる事業内容を示すものだけをできるだけ少なく選び、「事業に何が含まれるか」ではなく「事業が何であるか」で選ぶとしています。保有している設備を並べる目的では使いません。種目ごとの情報は、サイトの種目のページに文字で書いて伝えます。

テニススクールやゴルフ練習場を、施設とは別のビジネスプロフィールにできますか?

対外的に独立した組織として活動している部門なら、別のプロフィールを作れます。Googleのガイドラインは、こうした部門には通常、個別の顧客窓口があり、カテゴリも母体と異なり、営業時間が違うこともあると説明しています。名前は母体とも別の部門とも区別できるようにします。ガイドラインは売り場の一角のようなコーナーを作れない例に挙げているので、同じ受付で扱うコースを分けるためのプロフィールは、作らないのが無難です。

スポーツ施設の構造化データは、どの型を使えばよいですか?

Googleの店舗情報の構造化データの説明は、LocalBusinessのできるだけ具体的な下位の型を使うよう求めています。schema.orgでSportsActivityLocationの下にある型は、2026年10月7日の時点で BowlingAlley、ExerciseGym、GolfCourse、HealthClub、PublicSwimmingPool、SkiResort、SportsClub、StadiumOrArena、TennisComplex の9つです。合う型がない種目(フットサルなど)は、SportsActivityLocation のままにします。存在しない型の名前を書かないよう、公開前にschema.orgの語彙と照らします。

近くの市や駅の名前ごとに、種目のページを作ってもよいですか?

おすすめしません。Googleのスパムポリシーは、特定の地域や都市を対象にしたページを複数持ってユーザーを1つのページに誘導することや、階層がはっきりせず検索結果の一覧に近い、内容が似たページを並べることを、誘導ページの例に挙げています。施設がある場所は1か所なので、種目のページは1種目に1ページにし、近くの駅や地名は道案内として書きます。

この記事の書き方と確認の方針