メインコンテンツへスキップ

【Hugo×AWS】独自ドメインを設定する(Route53+ACM)

·
学習・作業ログ AWS Terraform Hugo
目次
Hugo-S3-CloudFrontで技術ブログを公開する - この記事は連載の一部です
パート 5: この記事

はじめに
#

この記事では、CloudFrontで配信している技術ブログに独自ドメインを設定します。 Route53でドメインを購入し、ACM(AWS Certificate Manager)でSSL/TLS証明書を発行してHTTPS通信を実現します。

この記事で構築するもの
#

  • Route53でのドメイン購入
  • ACM証明書の発行(DNS検証)
  • CloudFrontへのカスタムドメイン設定
  • Route53 Aliasレコードの設定

想定する読者
#

  • 前回までの記事でS3 + CloudFront + 監視 + ログ分析環境を構築済みの方
  • CloudFrontのデフォルトドメイン(dxxxxx.cloudfront.net)から独自ドメインに変更したい方
  • AWS初心者〜中級者

完成イメージ
#

独自ドメインを設定する(Route53+ACM)

【変更前】
https://dxxxxx.cloudfront.net/

【変更後】
https://example.com/  ← 独自ドメインでアクセス可能に

独自ドメインでHTTPSアクセスできるようになります。

シリーズ全体像
#

【Hugo×AWS】シリーズ全体で5記事投稿予定です。今回の記事は最終回の5本目です。

#タイトル内容
1Hugo + S3 + CloudFrontで技術ブログを公開するHugo環境構築〜手動デプロイまで
2GitHub Actions + OIDCで自動デプロイCI/CD構築、アクセスキー不要の認証
3CloudWatch + SNSで監視・アラート通知ダッシュボード、エラー率アラーム
4Athenaでアクセスログを分析するCloudFrontログのSQL分析
5独自ドメインを設定する(Route53 + ACM)カスタムドメイン、HTTPS

なぜ独自ドメインが必要なのか
#

CloudFrontのデフォルトドメイン(dxxxxx.cloudfront.net)でも技術的には問題ありませんが、独自ドメインには以下のメリットがあります。

観点デフォルトドメイン独自ドメイン
ブランディング△ AWSのサブドメイン✅ 自分の名前
覚えやすさ△ ランダムな文字列✅ 意味のある名前
信頼性△ 一時的に見える✅ 本格的に見える
SEO△ ドメインパワーなし✅ 独自ドメインで蓄積
ポートフォリオ△ 印象が弱い✅ プロフェッショナル

転職ポートフォリオとして見せる場合、独自ドメインは「本気で運用している」印象を与えます。

前提条件
#

必要な環境
#

本記事は、これまでの記事で以下が構築済みであることを前提としています。

  • CloudFront Distribution
  • Terraformプロジェクト

必要な情報
#

cd hugo-s3-demo-infra/prod
terraform output
cloudfront_distribution_id = "E2XXXXXXXXXX"
cloudfront_domain_name = "dxxxxx.cloudfront.net"

【重要】ACM証明書のリージョン制約
#

:::message alert CloudFrontでACM証明書を使う場合、必ず us-east-1(バージニア北部)で証明書を作成する必要があります。

東京リージョン(ap-northeast-1)で作成した証明書は、CloudFrontの設定画面で選択できません。 :::

なぜus-east-1なのか
#

CloudFrontはグローバルサービスであり、グローバルサービスは us-east-1 のACM証明書しか参照できない仕様です。

サービスACM証明書のリージョン
CloudFrontus-east-1のみ
ALBALBと同じリージョン
API Gateway(Edge)us-east-1のみ

AWS公式ドキュメントにも明記されています。

“To use an ACM certificate with Amazon CloudFront, you must request or import the certificate in the US East (N. Virginia) region.” — Supported Regions - AWS Certificate Manager

ドメインの購入(Route53)
#

Route53でドメインを購入する理由
#

購入先メリットデメリット
Route53AWSサービスとの連携が簡単、DNS検証が1クリック一部TLDは割高
お名前.com等安いTLDが多いネームサーバー移管が必要
Cloudflare.dev等が購入可能Route53との連携に手間

本記事ではRoute53での購入を前提に解説します。

:::message Route53では .dev ドメインを購入できません。.dev を使いたい場合は、Cloudflare等で購入後、Route53にネームサーバーを移管する必要があります。 :::

購入手順
#

  1. AWSコンソールで Route53 を開く
  2. 左メニューの「登録済みドメイン」→「ドメインの登録」
  3. 希望のドメイン名を検索(例: example
  4. 利用可能なTLDと価格を確認し、カートに追加
  5. 連絡先情報を入力
  6. 以下の設定を確認
    • プライバシー保護: 有効(WHOIS情報を非公開)
    • 自動更新: 有効(更新忘れ防止)
  7. 購入を完了

購入後の確認
#

購入後、以下のメールが届きます。

#メール件名対応
1Amazon Registrar にサインアップ〜対応不要
2[Action Required] Verify your email address⚠️ リンクをクリックして承認
3Registration of example.com succeeded対応不要(完了通知)

:::message alert 承認メールが迷惑メールフォルダに入っていることがあります。届かない場合は迷惑メールを確認してください。 :::

ホストゾーンの確認
#

ドメイン購入時に、Route53のホストゾーンが自動作成されます。

  1. Route53 → ホストゾーン
  2. 購入したドメイン名のホストゾーンを開く
  3. NS(ネームサーバー)とSOAレコードが存在することを確認

:::message ドメイン購入はTerraformで管理できません(一度きりの操作、連絡先入力が必要なため)。 ホストゾーンは data ソースで参照します。 :::

ACM証明書の発行
#

ACMとは
#

ACM(AWS Certificate Manager) は、SSL/TLS証明書を管理するAWSサービスです。

従来の方法ACM
証明書を購入(年間数千〜数万円)無料
手動でサーバーにインストールAWS側で自動設定
有効期限切れを手動管理自動更新

Terraformでのマルチリージョン設定
#

ACM証明書を us-east-1 で作成するため、マルチリージョンのprovider設定が必要です。

versions.tf の修正
#

terraform {
  required_version = ">= 1.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# デフォルトプロバイダー(東京リージョン)
provider "aws" {
  region = "ap-northeast-1"

  default_tags {
    tags = {
      Project   = var.project_name
      ManagedBy = "terraform"
    }
  }
}

# us-east-1プロバイダー(ACM証明書用)
provider "aws" {
  alias  = "us_east_1"
  region = "us-east-1"

  default_tags {
    tags = {
      Project   = var.project_name
      ManagedBy = "terraform"
    }
  }
}

alias = "us_east_1" を設定することで、リソースごとにプロバイダーを使い分けられます。

acm.tf
#

acm.tf を新規作成します。

# ===========================================
# ACM証明書(us-east-1で作成)
# ===========================================
resource "aws_acm_certificate" "main" {
  provider = aws.us_east_1  # us-east-1で作成

  domain_name               = var.domain_name
  subject_alternative_names = ["*.${var.domain_name}"]
  validation_method         = "DNS"

  lifecycle {
    create_before_destroy = true
  }

  tags = {
    Name = "${var.project_name}-certificate"
  }
}

# ===========================================
# DNS検証用レコード
# ===========================================
resource "aws_route53_record" "acm_validation" {
  for_each = {
    for dvo in aws_acm_certificate.main.domain_validation_options : dvo.domain_name => {
      name   = dvo.resource_record_name
      record = dvo.resource_record_value
      type   = dvo.resource_record_type
    }
  }

  zone_id = data.aws_route53_zone.main.zone_id
  name    = each.value.name
  type    = each.value.type
  records = [each.value.record]
  ttl     = 60

  allow_overwrite = true
}

# ===========================================
# 証明書の検証完了を待機
# ===========================================
resource "aws_acm_certificate_validation" "main" {
  provider = aws.us_east_1

  certificate_arn         = aws_acm_certificate.main.arn
  validation_record_fqdns = [for record in aws_route53_record.acm_validation : record.fqdn]
}

コード解説
#

証明書リソース
#

resource "aws_acm_certificate" "main" {
  provider = aws.us_east_1  # 重要: us-east-1で作成

  domain_name               = var.domain_name          # example.com
  subject_alternative_names = ["*.${var.domain_name}"] # *.example.com(ワイルドカード)
  validation_method         = "DNS"                    # DNS検証
}
属性説明
providerus-east-1プロバイダーを指定
domain_nameメインドメイン
subject_alternative_namesサブドメイン(ワイルドカードで全て対応)
validation_methodDNS検証(Route53なら自動化可能)

DNS検証レコード
#

resource "aws_route53_record" "acm_validation" {
  for_each = {
    for dvo in aws_acm_certificate.main.domain_validation_options : dvo.domain_name => { ... }
  }
  # ...
}

ACMがDNS検証用に要求するCNAMEレコードを、Route53に自動作成します。 for_each でドメインごとにレコードを作成します(メインドメイン + ワイルドカード)。

検証完了の待機
#

resource "aws_acm_certificate_validation" "main" {
  certificate_arn         = aws_acm_certificate.main.arn
  validation_record_fqdns = [for record in aws_route53_record.acm_validation : record.fqdn]
}

証明書の検証が完了するまで待機するリソースです。 これにより、証明書が「発行済み」になるまでTerraformが次のリソース作成を待ちます。

variables.tf に追記
#

variable "domain_name" {
  description = "Domain name for the website"
  type        = string
}

terraform.tfvars に追記
#

domain_name = "example.com"  # 購入したドメイン名に置き換え

Route53の設定
#

route53.tf
#

route53.tf を新規作成します。

# ===========================================
# ホストゾーンの参照(ドメイン購入時に自動作成済み)
# ===========================================
data "aws_route53_zone" "main" {
  name         = var.domain_name
  private_zone = false
}

# ===========================================
# Aliasレコード(CloudFrontへのルーティング)
# ===========================================
resource "aws_route53_record" "main" {
  zone_id = data.aws_route53_zone.main.zone_id
  name    = var.domain_name
  type    = "A"

  alias {
    name                   = aws_cloudfront_distribution.main.domain_name
    zone_id                = aws_cloudfront_distribution.main.hosted_zone_id
    evaluate_target_health = false
  }
}

# wwwサブドメイン用(任意)
resource "aws_route53_record" "www" {
  zone_id = data.aws_route53_zone.main.zone_id
  name    = "www.${var.domain_name}"
  type    = "A"

  alias {
    name                   = aws_cloudfront_distribution.main.domain_name
    zone_id                = aws_cloudfront_distribution.main.hosted_zone_id
    evaluate_target_health = false
  }
}

# ===========================================
# Outputs
# ===========================================
output "website_url" {
  value = "https://${var.domain_name}"
}

コード解説
#

ホストゾーンの参照
#

data "aws_route53_zone" "main" {
  name         = var.domain_name
  private_zone = false
}

ドメイン購入時に自動作成されたホストゾーンを data ソースで参照します。

Aliasレコード
#

resource "aws_route53_record" "main" {
  type = "A"

  alias {
    name                   = aws_cloudfront_distribution.main.domain_name
    zone_id                = aws_cloudfront_distribution.main.hosted_zone_id
    evaluate_target_health = false
  }
}

Aliasレコードは、AWSサービス(CloudFront、ALB、S3等)への特殊なルーティングです。

通常のAレコードAliasレコード
IPアドレスを指定AWSサービスのドメイン名を指定
TTLを設定TTL不要(自動)
DNSクエリ課金あり無料

CloudFrontへのルーティングには、Aliasレコードを使用するのがベストプラクティスです。

CloudFrontの更新
#

CloudFront Distributionにカスタムドメインと証明書を設定します。

main.tf の修正
#

aws_cloudfront_distribution リソースを修正します。

resource "aws_cloudfront_distribution" "main" {
  # ...既存の設定...

  # カスタムドメインを追加
  aliases = [var.domain_name, "www.${var.domain_name}"]

  # viewer_certificate を修正
  viewer_certificate {
    acm_certificate_arn      = aws_acm_certificate.main.arn
    ssl_support_method       = "sni-only"
    minimum_protocol_version = "TLSv1.2_2021"
  }

  # 証明書の検証完了を待ってから作成
  depends_on = [aws_acm_certificate_validation.main]

  # ...既存の設定...
}

コード解説
#

aliases
#

aliases = [var.domain_name, "www.${var.domain_name}"]

CloudFrontが受け付けるドメイン名を指定します。 example.comwww.example.com の両方からアクセス可能になります。

viewer_certificate
#

viewer_certificate {
  acm_certificate_arn      = aws_acm_certificate.main.arn
  ssl_support_method       = "sni-only"
  minimum_protocol_version = "TLSv1.2_2021"
}
属性説明
acm_certificate_arnACM証明書のARN
ssl_support_methodsni-only(SNI対応クライアントのみ、追加料金なし)
minimum_protocol_version最小TLSバージョン(セキュリティ向上のため1.2以上推奨)

:::message ssl_support_method = "vip" を選択すると、専用IPアドレスが割り当てられますが、月額$600の追加料金が発生します。 個人ブログでは sni-only で十分です。 :::

depends_on
#

depends_on = [aws_acm_certificate_validation.main]

証明書の検証が完了してから、CloudFront Distributionを作成/更新するようにします。

Hugoの設定更新
#

hugo.tomlbaseURL を独自ドメインに更新します。

baseURL = 'https://example.com/'
languageCode = 'en-us'
title = 'My Tech Blog'
theme = 'ananke'

更新後、pushすればGitHub Actionsが自動デプロイします。

実行
#

terraform plan
terraform apply

:::message ACM証明書の検証には数分〜数十分かかる場合があります。 terraform apply が長時間待機状態になっても、正常に処理が進んでいます。 :::

動作確認
#

1. 証明書のステータス確認
#

AWSコンソール → ACM(us-east-1リージョン)で、証明書のステータスが「発行済み」になっていることを確認します。

2. ブラウザでアクセス
#

以下のURLにアクセスし、正常に表示されることを確認します。

  • https://example.com/
  • https://www.example.com/

確認ポイント:

  • サイトが正常に表示される
  • ブラウザのアドレスバーに鍵マークが表示される(HTTPS)
  • 記事詳細ページにもアクセスできる

3. HTTPリダイレクト確認
#

http://example.com/ にアクセスし、https://example.com/ にリダイレクトされることを確認します。 (CloudFrontの viewer_protocol_policy = "redirect-to-https" により自動リダイレクト)

コストについて
#

Route53
#

項目料金
ドメイン(.com)$15/年
ホストゾーン$0.50/月
DNSクエリ(Alias)無料

ACM
#

項目料金
パブリック証明書無料
自動更新無料

独自ドメインの追加コストは、ドメイン代 + ホストゾーン代で月額約200円程度です。

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

証明書がCloudFrontの選択肢に表示されない
#

原因: 証明書が us-east-1 以外のリージョンで作成されている

対処法:

  1. AWSコンソールで現在の証明書のリージョンを確認
  2. us-east-1 以外の場合は、証明書を削除して us-east-1 で再作成

DNS検証が完了しない
#

原因: DNS検証レコードが正しく作成されていない

確認ポイント:

  1. Route53のホストゾーンにCNAMEレコードが作成されているか確認
  2. レコードの値がACMの要求と一致しているか確認

対処法: terraform apply を再実行

サイトにアクセスできない
#

原因: DNSの伝播に時間がかかっている

対処法:

  1. dig example.com でDNSレコードを確認
  2. 数分〜数時間待ってから再度アクセス

ERR_SSL_VERSION_OR_CIPHER_MISMATCH
#

原因: 証明書の設定が正しくない

確認ポイント:

  1. CloudFrontの aliases に正しいドメイン名が設定されているか
  2. ACM証明書のドメイン名が一致しているか

まとめ
#

本記事では、以下を構築しました。

  • Route53: ドメイン購入、DNSレコード管理
  • ACM: SSL/TLS証明書の発行(us-east-1)
  • CloudFront: カスタムドメイン設定
  • Terraform: マルチリージョンprovider設定

作成したリソース
#

リソース用途
Route53 AliasレコードCloudFrontへのルーティング
Route53 CNAMEレコードACM DNS検証
ACM証明書HTTPS通信の実現
CloudFront aliasesカスタムドメインの受け付け

重要ポイント
#

項目ポイント
ACM証明書のリージョンus-east-1必須
AliasレコードCloudFrontへのルーティングは無料
ssl_support_methodsni-only で追加料金なし
DNS伝播最大72時間かかる場合がある

これで【Hugo×AWS】シリーズは完了です!

5記事を通じて、以下の技術ブログ基盤を構築しました。

  1. 静的サイトホスティング: S3 + CloudFront
  2. 自動デプロイ: GitHub Actions + OIDC
  3. 監視・アラート: CloudWatch + SNS + AWS Budgets
  4. ログ分析: Athena + Glue
  5. 独自ドメイン: Route53 + ACM

すべてTerraformでコード化されているため、再現性があり、ポートフォリオとしても活用できます。

参考資料
#

Hugo-S3-CloudFrontで技術ブログを公開する - この記事は連載の一部です
パート 5: この記事