🕌

Next.jsのデプロイ手順|Vercel・独自ドメイン・SEO対応

に公開

Next.js で作成したポートフォリオを Vercel に公開し、独自ドメインを設定して、基本的な SEO まで整える手順をまとめます。

ポートフォリオ本体はこちら → 劉宗耀のポートフォリオ(英語版)

目次

前提条件

デプロイを進めるにあたり、以下のものが必要です:

  • GitHubアカウント
  • Next.jsプロジェクト(ポートフォリオサイト)
  • Vercelアカウント(無料で登録可能)
  • ドメインレジストラアカウント

Vercelへのデプロイ

Vercel は Next.js と相性のよいホスティング基盤で、ビルドから配信までを最適化してくれます。デプロイの手順は非常に簡単です。

ステップ1: GitHubリポジトリにプロジェクトをプッシュ

まず、Next.jsプロジェクトをGitHubリポジトリにプッシュします。

# ローカルリポジトリの初期化(まだ行っていない場合)
git init

git add .
git commit -m "Initial commit"

git remote add origin https://github.com/your-username/portfolio.git
git push -u origin main

ステップ2: VercelアカウントでGitHubとの接続を許可

Vercelのウェブサイトにアクセスし、アカウントを作成します。その後、GitHubとの接続を許可します。

ステップ3: 新しいプロジェクトのインポート

Vercelのダッシュボードで「Add New...」-「project」をクリックし、先ほどのGitHubリポジトリを選択します。

ステップ4: デプロイ設定

プロジェクトの設定を確認します:

  • Framework Preset: Next.js(自動的に検出されます)
  • Root Directory: プロジェクトのルートディレクトリ
  • Build Command: npm run build(デフォルト)
  • Output Directory: .next(デフォルト)

環境変数が必要な場合は、ここで設定できます。

PS:先にローカル環境でnpm run buildをチェックするのをおすすめです!エラが出やすいですから。

ステップ5: デプロイ

「Deploy」ボタンをクリックすると、Vercelは自動的にアプリケーションをビルドしてデプロイします。完了すると、your-project.vercel.appのような一時的なURLが提供されます。

この後コードを変更してgithubへプッシュしたら自動的に再デプロイしてくれるから、便利です。

ドメインの購入と設定

独自のドメインを購入し、Vercelプロジェクトに設定することで、よりプロフェッショナルな印象を与えることができます。

ステップ1: ドメインの購入

好みのドメインレジストラでドメインを購入します:

  • 適切なトップレベルドメイン(TLD)を選択(.com、.dev、.meなど)

ステップ2: Vercelでのドメイン設定

  1. Vercelのプロジェクトダッシュボードにアクセスします
  2. 「Settings」>「Domains」に移動します
  3. 購入したドメイン名を入力し、「Add」をクリックします
  4. VercelはDNSレコードの設定ガイダンスを表示します

ステップ3: DNSレコードの設定

ドメインレジストラの管理画面にアクセスし、Vercelが指示する通りにDNSレコードを設定します。通常、次のようなレコードを追加します:

  • Aレコード: 76.76.21.21(VercelのIPアドレス)
  • CNAMEレコード: cname.vercel-dns.com(wwwサブドメイン用)

設定完了後、反映されるのは時間がかかりますので、お待ち下さい。

SEOの基本設定

検索エンジン最適化(SEO)は、ウェブサイトが検索結果の上位に表示されるようにするための重要な要素です。Next.jsのApp Routerを使用して基本的なSEO設定を行いましょう。

メタデータの設定

Next.jsのApp Routerでは、app/layout.tsxファイルでメタデータを設定できます。

// app/layout.tsx
import './global.css';
import '../components/performance.css';
import { ThemeProvider } from '../context/ThemeContext';
import Header from '../components/Header';
import { getDictionary } from './i18n';
import LanguageSetter from '../components/LanguageSetter';
import type { Metadata, Viewport } from 'next';
import Script from 'next/script';

const SITE_URL = (process.env.NEXT_PUBLIC_SITE_URL ?? 'https://liuzongyao.work').replace(/\/$/, '');

export const metadata: Metadata = {
  metadataBase: new URL(SITE_URL),
  title: {
    default: 'LIU ZONGYAO | Kyushu University M1 – Aspiring Application Engineer',
    template: '%s | LIU ZONGYAO',
  },
  description:
    'LIU ZONGYAO (劉宗耀 / 刘宗耀). M1 at Kyushu University. Portfolio and profile of an aspiring application engineer.',
  authors: [{ name: 'LIU ZONGYAO', url: SITE_URL }],
  // keywords そんなに効かないので、適当に入ればいいです。
  keywords: ['LIU ZONGYAO', '劉宗耀', '九州大学', 'Kyushu University', 'Application Engineer', 'Portfolio', 'ポートフォリオ'],
  alternates: {
    canonical: SITE_URL,
     languages: { en: `${SITE_URL}/en`, ja: `${SITE_URL}/ja` }
  },
  openGraph: {
    title: 'LIU ZONGYAO | Kyushu University M1 – Aspiring Application Engineer',
    description:
      'Portfolio and profile of LIU ZONGYAO (M1 at Kyushu University), aspiring application engineer.',
    url: SITE_URL,
    siteName: 'LIU ZONGYAO Portfolio',
    type: 'website',
    images: [{ url: `${SITE_URL}/og.png`, width: 1200, height: 630, alt: 'LIU ZONGYAO Portfolio' }],
    locale: 'en_US', 
  },
  twitter: {
    card: 'summary_large_image',
    title: 'LIU ZONGYAO | Kyushu University M1 – Aspiring Application Engineer',
    description: 'Portfolio and profile.',
    images: [`${SITE_URL}/og.png`],
  },
  robots: { index: true, follow: true },
  icons: {
    icon: '/favicon.ico',
    apple: '/apple-touch-icon.png',
  },
};

export const viewport: Viewport = {
  width: 'device-width',
  initialScale: 1,
};

export default async function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const dictionary = await getDictionary('en');
  const personJsonLd = {
    '@context': 'https://schema.org',
    '@type': 'Person',
    name: 'LIU ZONGYAO',
    alternateName: ['劉宗耀', '刘宗耀'],
    affiliation: { '@type': 'CollegeOrUniversity', name: 'Kyushu University' },
    jobTitle: 'Aspiring Application Engineer',
    url: SITE_URL,
    sameAs: [
       'https://github.com/ACAne-me',
       'https://zenn.dev/acanoneko',
       'https://qiita.com/ACAne',
       'https://x.com/cape_kaka5705',
    ],
  };

const currentLocale = locale ?? 'en';
  
  return (
    <html lang={currentLocale}>
      <body>
        <Script
          id="ld-person"
          type="application/ld+json"
          dangerouslySetInnerHTML={{ __html: JSON.stringify(personJsonLd) }}
        />
        <ThemeProvider>
          <LanguageSetter locale={currentLocale} />
          <Header locale={currentLocale} dictionary={dictionary} />
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

サイトマップの作成

サイトマップは、検索エンジンがウェブサイトのページを効率的にクロールできるようにするXMLファイルです。Next.jsでは、app/sitemap.tsファイルを作成して動的にサイトマップを生成できます。

// app/sitemap.ts
import type { MetadataRoute } from 'next';
const SITE = (process.env.NEXT_PUBLIC_SITE_URL ?? 'https://liuzongyao.work').replace(/\/$/, '');

export default function sitemap(): MetadataRoute.Sitemap {
  const now = new Date();
  return [
    { url: `${SITE}/`, lastModified: now, changeFrequency: 'monthly', priority: 1 },
    { url: `${SITE}/about`, lastModified: now, changeFrequency: 'monthly', priority: 0.8 },
    { url: `${SITE}/projects`, lastModified: now, changeFrequency: 'weekly',  priority: 0.9 },
  ];
}

robots.txtの設定

robots.txtファイルは、検索エンジンのクローラーに対して、ウェブサイトのどのページをクロールするか、どのページをクロールしないかを指示します。public/robots.txtファイルを作成します。

// app/robots.ts
import type { MetadataRoute } from 'next';

const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? 'https://liuzongyao.work';

export default function robots(): MetadataRoute.Robots {
  return {
    rules: { userAgent: '*', allow: '/' },
    sitemap: `${SITE_URL.replace(/\/$/, '')}/sitemap.xml`,
    host: SITE_URL.replace(/\/$/, ''),
  };
}

多言語サポートのSEO対策

多言語サイトの場合は、各言語バージョンの関連付けが重要です。app/layout.tsxのメタデータでalternates.languagesを設定します。

alternates: {
    canonical: SITE_URL,
     languages: { en: `${SITE_URL}/en`, ja: `${SITE_URL}/ja` }
  },

また、HTMLタグに言語属性を追加します:

// app/layout.tsxのRootLayout関数内
return (
  <html lang='en'>
    {/* ... */}
  </html>
);

Google Search Consoleの設定

Google Search Consoleは、ウェブサイトの検索トラフィックとパフォーマンスを監視・管理するための無料ツールです。

ステップ1: プロパティの追加

Google Search Consoleにアクセスし、「プロパティを追加」をクリックします。URLプレフィックスを選択し、ドメイン(例:https://your-domain.com)を入力します。

ステップ2: 所有権の検証

所有権の確認は DNS(TXT レコード) がシンプルです。ドメインの DNS に値を1つ追加するだけで、ファイル配置や再デプロイは不要です。

Search Console の「所有権の確認」で DNS レコード を選択し、表示された
TXT レコード(_google-site-verification=xxxx...) の値をコピーします。

ドメインの DNS 管理画面で、以下を追加します。

種別:TXT

ホスト名:@(または空欄/ルート。プロバイダ仕様に従う)

値:google-site-verification=xxxxxxxx...

TTL:デフォルトのままでOK
※ www などのサブドメインではなく、ルート(apex)側に追加します。

変更を保存して DNS 反映を待機(目安:約10分。環境によってはもう少しかかることもあります)。

Search Console に戻り「確認」をクリックします。成功すれば所有権が有効化されます。

ステップ3: インデックスに登録

確認中です!

まとめ

この記事では、Next.jsポートフォリオサイトをVercelにデプロイし、独自のドメインを設定し、SEO対策を施すまでの手順を記録しました。主なポイントは以下の通りです:

  1. VercelへのデプロイとGitHubリポジトリとの接続
  2. 独自のドメインを購入し、DNSレコードを設定すること
  3. メタデータ、サイトマップ、robots.txtの設定でSEOを最適化できる
  4. Google Search Consoleを活用してサイトのパフォーマンスを監視し、改善できる

これらのステップを実行することで、ポートフォリオサイトが検索エンジンで見つけやすくなるはずです、もしご指摘があればぜひお願いします!

Discussion