3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

🚧 AWS WAFで簡単にメンテナンスモードを実現する方法(CloudFormation付き)

Last updated at Posted at 2025-11-14

🎯 はじめに

AWS WAF を使って 「一時的にサイトをメンテナンス表示にしたい」 と思ったことはありませんか?
本記事では、既存の WAF 構成とは独立して 「メンテナンス専用 WAF」 を作成し、ホワイトリスト以外のアクセスを 503 エラー+メンテナンスページ に切り替える方法を紹介します。

CloudFormationテンプレート付きで、パラメータ 1 つで ON / OFF 切り替え可能です。

🧩 できること

✅ メンテナンスモードを ON にすると、全ユーザーがメンテナンスページへリダイレクト( HTTP 503 )
✅ 特定の IP(社内・ VPN など)だけは通常アクセス許可
✅ メンテナンス解除はパラメータ 1 つ変更するだけ
✅ CloudFront / ALB どちらにも対応( Scope 切替)
✅ HTML をテンプレート内に埋め込み済み(スタイル調整も可能)

🏗️ 構成イメージ

💡 動作の流れ

優先度 ルール名 条件 アクション
0 Allow-Whitelist 許可 IPSet に一致 Allow
1 Maintenance-Block 常に一致( MaintenanceMode = ON の時のみ有効) Block + 503 + メンテHTML

⚙️ CloudFormationテンプレート

以下のテンプレートを利用します。

AWS WAF (MaintenanceMode)
AWSTemplateFormatVersion: "2010-09-09"
Description: Minimal WAF for Maintenance Mode only (whitelist = allow, others = 503 HTML)

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label: { default: WAF Configuration }
        Parameters:
          - EnvTagPrefix
          - ProjTagPrefix
          - Scope
          - MaintenanceMode

Parameters:
  EnvTagPrefix:
    Type: String
    Default: stg
  ProjTagPrefix:
    Type: String
    Default: app
  Scope:
    Type: String
    Default: CLOUDFRONT
    AllowedValues: [REGIONAL, CLOUDFRONT]
  MaintenanceMode:
    Type: String
    Default: 'OFF'
    AllowedValues: ['ON', 'OFF']

Conditions:
  IsMaintenanceMode: !Equals [!Ref MaintenanceMode, 'ON']

Resources:
  WAFLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "aws-waf-logs-${EnvTagPrefix}-${ProjTagPrefix}"
      RetentionInDays: 60

  IPWhiteList:
    Type: AWS::WAFv2::IPSet
    Properties:
      Name: !Sub ${EnvTagPrefix}-${ProjTagPrefix}-ip-whitelist
      Description: Allowed IPs during maintenance
      Scope: !Ref Scope
      IPAddressVersion: IPV4
      Addresses:
        - 1.1.1.1/32

  WebAcl:
    Type: AWS::WAFv2::WebACL
    Properties:
      Name: !Sub ${EnvTagPrefix}-${ProjTagPrefix}-MaintenanceOnly
      Scope: !Ref Scope
      DefaultAction: { Allow: {} }
      VisibilityConfig:
        CloudWatchMetricsEnabled: true
        SampledRequestsEnabled: true
        MetricName: !Sub ${EnvTagPrefix}-${ProjTagPrefix}-maintenance
      CustomResponseBodies:
        MaintenanceHtml:
          ContentType: TEXT_HTML
          Content: |
            <!doctype html><html lang="ja"><head><meta charset="utf-8">
            <meta name="viewport" content="width=device-width,initial-scale=1">
            <title>メンテナンス中</title>
            <style>
              body{margin:0;min-height:100vh;display:flex;align-items:center;justify-content:center;
                   font-family:system-ui,-apple-system,Segoe UI,Roboto,"Noto Sans JP",sans-serif;background:#0b1220;color:#e8eefc}
              .card{max-width:720px;padding:24px;border-radius:14px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08)}
              h1{margin:0 0 8px;font-size:20px}p{margin:8px 0 0;line-height:1.7;color:#bcd1f4}
            </style></head>
            <body><main class="card"><h1>🔧 メンテナンス中</h1>
            <p>現在、システムメンテナンスを実施しております。しばらくしてから再度アクセスしてください。</p>
            </main></body></html>
      Rules:
        - Name: !Sub ${EnvTagPrefix}-${ProjTagPrefix}-Allow-Whitelist
          Priority: 0
          Statement:
            IPSetReferenceStatement:
              Arn: !GetAtt IPWhiteList.Arn
          Action: { Allow: {} }
          VisibilityConfig:
            CloudWatchMetricsEnabled: true
            SampledRequestsEnabled: true
            MetricName: !Sub ${EnvTagPrefix}-${ProjTagPrefix}-Allow-Whitelist

        - !If
          - IsMaintenanceMode
          - Name: !Sub ${EnvTagPrefix}-${ProjTagPrefix}-Maintenance-Block
            Priority: 1
            Statement:
              NotStatement:
                Statement:
                  SizeConstraintStatement:
                    FieldToMatch: { SingleHeader: { Name: host } }
                    ComparisonOperator: LT
                    Size: 0
                    TextTransformations: [{ Priority: 0, Type: NONE }]
            Action:
              Block:
                CustomResponse:
                  ResponseCode: 503
                  CustomResponseBodyKey: MaintenanceHtml
                  ResponseHeaders:
                    - { Name: Retry-After, Value: '3600' }
                    - { Name: Cache-Control, Value: 'no-cache, no-store, must-revalidate' }
            VisibilityConfig:
              CloudWatchMetricsEnabled: true
              SampledRequestsEnabled: true
              MetricName: !Sub ${EnvTagPrefix}-${ProjTagPrefix}-Maintenance-Block
          - !Ref "AWS::NoValue"

  WAFLogConfig:
    Type: AWS::WAFv2::LoggingConfiguration
    Properties:
      LogDestinationConfigs:
        - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:${WAFLogGroup}"
      ResourceArn: !GetAtt WebAcl.Arn

Outputs:
  WebAclArn:
    Value: !GetAtt WebAcl.Arn
  WebAclId:
    Value: !GetAtt WebAcl.Id

🔍 動作イメージ

状態 挙動
MaintenanceMode = ON Whitelist 以外は503でメンテナンスページ表示
MaintenanceMode = OFF 全アクセス許可(通常運用)

メンテ切替はパラメータ 1 つの変更だけで即時反映されます。
WAF の設定は高速に反映されるため、実運用でも安心して使えます。

📷 サンプル画像

maintenance-page-example.png

🧠 コード解説

✅ 常時許可ルール(Allow-Whitelist)

Statement:
  IPSetReferenceStatement:
    Arn: !GetAtt IPWhiteList.Arn
Action: { Allow: {} }

→ IPSet に登録された IP アドレスからのアクセスは常に許可します。

⚠️ メンテナンスブロックルール(Maintenance-Block)

NotStatement:
  Statement:
    SizeConstraintStatement:
      FieldToMatch: { SingleHeader: { Name: host } }
      ComparisonOperator: LT
      Size: 0

→ 「host ヘッダのサイズが 0 未満」というあり得ない条件の否定、つまり常に一致します。
Whitelist で除外された全トラフィックがここにヒットし、503 を返します。

💬 カスタムレスポンス

ResponseCode: 503
CustomResponseBodyKey: MaintenanceHtml
ResponseHeaders:
  - { Name: Retry-After, Value: '3600' }
  - { Name: Cache-Control, Value: 'no-cache, no-store, must-revalidate' }

→ 1 時間後に再試行を促し、キャッシュを防止するヘッダーを付与しています。

🧾 注意点

  • CloudFront 用の場合は us-east-1 で作成する必要があります。
  • ALB や API Gateway など Regional WAF の場合は対象リージョンで OK。
  • 503 のキャッシュを避けたい場合は、CloudFront のエラーページ TTL も短く設定しましょう。
  • 監視やヘルスチェック用の IP は IPWhiteList に追加しておくと便利です。

🧰 応用アイデア

  • 特定パスだけ許可(例:/status)を追加して監視ルートを残す
  • 運用チームごとに IPSet を分離
  • カスタム HTML をブランド仕様に差し替え(ロゴ・CSS 調整)

🚀 まとめ

ポイント 内容
シンプルさ ルール 2 つ+パラメータ1つで構成
安全性 Whitelist で関係者だけ通す
即時反映 MaintenanceMode を切り替えるだけで即反映
拡張性 CloudFront / ALB / API Gateway すべて対応

おわりに

このテンプレートはメンテナンスだけの内容ですが、すでに導入している AWS WAF があればそこへの組み込みも簡単に行えると思います。


📎 参考

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?