CLOUD NAVIクラウドとは?からクラウドを支える技術や関連用語まで解説

FJcloud実践

Fastlyとオブジェクトストレージの組み合わせで静的ウェブサイト配信システムを作る方法

2025年8月29日
Fastlyとオブジェクトストレージの組み合わせで静的ウェブサイト配信システムを作る方法

静的なウェブサイトを配信するためだけにサーバーの維持管理をするの大変ですよね。
今回は、そんなお困りごとに対応する方法として「Fastly+オブジェクトストレージ」によるシステム構築を紹介します。

オブジェクトストレージとFastlyの組み合わせ

オブジェクトストレージは、分散保存と冗長化により高い可用性と耐久性を実現するデータ基盤です。
HTTPアクセスによるデータ配信が可能でHTMLファイル配信基盤としても利用でき、アクセス増加にも自動スケールで対応できます。
この特徴は静的コンテンツの配信をウェブサーバーの構築なしに実現できます。

ただし、そのままではユーザーはオブジェクトストレージのもつURLへアクセスすることになり、だれが提供している情報なのかがわからなくなります。

そこでFastlyをはじめとするCDNを利用し、オブジェクトストレージのURLではなく、自社ドメインでの配信を実施します。

機能・サービスの紹介

  • Fastly
    独自システムと高スペックインフラによる瞬時のキャッシュ消去/更新で、従来のCDNでは実現が難しかった動的コンテンツをキャッシュできるCDNサービスです。
  • オブジェクトストレージサービス
    オブジェクトストレージサービスは大容量ファイルの保管場所として、容量を気にすることなく利用できるストレージサービスです。
    インターフェイスとしてREST APIを提供しています。

検証

事前準備

構築手順

  1. バケットの作成と設定
    まず、オブジェクトストレージへウェブサイトのコンテンツを格納するためのバケットを作成します。
    今回はjp-east-1リージョンのオブジェクトストレージに以下のパラメーターで作成します。パラメーターは必要に応じて変更してください。
    • バケット名:public-site
    • リージョン:jp-east-1
    1. オブジェクトストレージサービスの画面からアクセスキー、シークレットキーを入手します。
    2. 環境変数に設定します。
      # export AWS_ACCESS_KEY_ID=your_access_key
      # export AWS_SECRET_ACCESS_KEY=your_secret_key
    3. s3コマンドを利用し、バケットを作成します。この時、エンドポイントを指定し、FJcloud-Vのオブジェクトストレージを対象に操作します。
      # aws --endpoint-url https://jp-east-1.storage.api.nifcloud.com s3api create-bucket --bucket public-site
    4. 想定外のアクセスを防ぐため、パケットへ、パケット操作に認証を必要とするポリシーを設定します。
      • テナントIDをオブジェクトストレージのダッシュボードより入手します。
      • 以下の内容を記載したファイル、「acl」を作成します。この時「[YOUR-TENANT-ID]」を先に取得したテナントIDへ、「[YOUR-ID]」を自身のID(例ABC00000)へ変更します。
        {
        "Statement": [
          {
            "Sid": "Allow",
            "Effect": "Allow",
            "Principal": {
              "SGWS": "urn:sgws:identity::[YOUR-TENANT-ID]:user/[YOUR-ID]"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:sgws:s3:::public-site/*",
            "Condition": {
            }
          },
          {
            "Sid": "Allow",
            "Effect": "Allow",
            "Principal": {
            "SGWS": "urn:sgws:identity::[YOUR-TENANT-ID]:user/[YOUR-ID]"
            },
            "Action": "s3:DeleteObject",
            "Resource": "arn:sgws:s3:::public-site/*",
            "Condition": {
            }
          },
          {
            "Sid": "Allow",
            "Effect": "Allow",
            "Principal": {
            "SGWS": "urn:sgws:identity::[YOUR-TENANT-ID]:user/[YOUR-ID]"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:sgws:s3:::public-site/*",
            "Condition": {
            }
          }
         ]
        }
      • 作成したパケットに適用します。
        # aws --endpoint-url https://jp-east-1.storage.api.nifcloud.com s3api put-bucket-policy --bucket public-site --policy file://./acl
      • 正常に適用されたか確認します。
        # aws --endpoint-url https://jp-east-1.storage.api.nifcloud.com s3api get-bucket-policy --bucket public-site
        
        {
            "Policy": "{\"Statement\":[{\"Sid\":\"Allow\",\"Effect\":\"Allow\",\"Principal\":{\"SGWS\":\"urn:sgws:identity::[YOUR-TENANT-ID]:user/[YOUR-ID]\"},\"Action\":\"s3:GetObject\",
        	\"Resource\":\"arn:sgws:s3:::public-site/*\",\"Condition\":{}},{\"Sid\":\"Allow\",\"Effect\":\"Allow\",\"Principal\":{\"SGWS\":\"urn:sgws:identity::[YOUR-TENANT-ID]:user/[YOUR-ID]\"},
        	\"Action\":\"s3:DeleteObject\",\"Resource\":\"arn:sgws:s3:::public-site/*\",\"Condition\":{}},{\"Sid\":\"Allow\",\"Effect\":\"Allow\",\"Principal\":{\"SGWS\":\"urn:sgws:identity::[YOUR-TENANT-ID]:user/
        	[YOUR-ID]\"},\"Action\":\"s3:PutObject\",\"Resource\":\"arn:sgws:s3:::public-site/*\",\"Condition\":{}}]}"
        }
  2. コンテンツのアップロード
    作成したバケットに、WebサイトのHTML、CSS、JavaScript、画像などの静的コンテンツをアップロードします。
    • コンテンツの作成
      今回は、以下の内容を記載したファイル「index.html」と画像「top.png」を作成しておきます。
      <!DOCTYPE html>
      <html lang="ja">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>index</title>
      </head>
      <body>
          <h1>index</h1>
          <img src="/top.png">
      </body>
      </html>
    • コンテンツのアップロード
      コンテンツのアップロードはs3apiではなく、s3コマンドのサブコマンドcpを利用します。
      この時、ファイルがhtmlファイルであるため、コンテンツタイプを指定してアップロードします。他のファイルをアップロードする際は適切なタイプを指定してください。
      アップロードするパスは、「domain.hoge/index.html」の場合、「バケット名/index.html」へアップロードしてください。
      # aws --endpoint-url https://jp-east-1.storage.api.nifcloud.com s3 cp index.html s3://public-site/index.html --content-type text/html
      # aws --endpoint-url https://jp-east-1.storage.api.nifcloud.com s3 cp top.png s3://public-site/top.png --content-type image/png
  3. Fastlyサービスの作成と設定
    1. Fastlyにログインし、「CDN services」から、「Create service」をクリックし、作成画面に入ります。
      service create
    2. Fastlyで新しいサービスを作成します。下記パラメーターを入力し「Save Draft」をクリックします。
      • サービス名:test
      • オリジンホスト名: FJcloud-Vオブジェクトストレージのエンドポイント。今回は jp-east-1.storage.api.nifcloud.com を指定します。
      • ドメイン:ホストするサイトのURLを入力します。今回は「your-domain.com」としています。
        service config
    3. VCL (Varnish Configuration Language) snippetsの設定
      FastlyのVCLを利用し、オブジェクトストレージアクセスへの認証および、アクセスパスの修正を実施します。
      • 左の「VCL snippets」を選択し、「Create your first VCL snippet」をクリック、作成画面に入ります。
        first vcl snippets
      • 「Name」に適切な名前(redirect-obje-storage等)を付与し、「within subroutine」を選択、ドロップダウンから「miss(vcl_miss)」を選択します。
        first vcl snippets 2
      • 「VCL」に以下のコードを入力し、画面下の「Create」をクリックして保存します。 (要変更)となっている部分は利用時に変更してください。 スクリプトの内容は、ユーザーIDによる認証情報の付与と、アクセス先の読み替えを実施しています。
        declare local var.fjvAccessKey STRING;
        declare local var.fjvSecretKey STRING;
        declare local var.fjvS3Bucket STRING;
        declare local var.fjvRegion STRING;
        declare local var.fjvS3Host STRING;
        declare local var.canonicalHeaders STRING;
        declare local var.signedHeaders STRING;
        declare local var.canonicalRequest STRING;
        declare local var.canonicalQuery STRING;
        declare local var.stringToSign STRING;
        declare local var.dateStamp STRING;
        declare local var.signature STRING;
        declare local var.scope STRING;
        declare local var.bk-host STRING;
        declare local var.bk-path STRING;
        declare local var.bk-test STRING;
                  
        set var.fjvAccessKey = "OBJECTSTORAGE_ACCESS_KEY";   # アクセスキー(要変更)
        set var.fjvSecretKey = "OBJECTSTORAGE_SECRET_KEY";   # シークレットキー(要変更)
        set var.fjvS3Bucket = "public-site";   # バケット名(要変更)
        set var.fjvRegion = "jp-east-1";   # 利用しているオブジェクトストレージのリージョン(要変更)
        set var.fjvS3Host = var.fjvRegion ".storage.api.nifcloud.com";
                  
        if (req.method == "GET" && !req.backend.is_shield) {
                  
          set bereq.http.x-amz-content-sha256 = digest.hash_sha256("");
          set bereq.http.x-amz-date = strftime({"%Y%m%dT%H%M%SZ"}, now);
          set bereq.http.host = var.fjvS3Host;
          set bereq.url = "/" var.fjvS3Bucket querystring.remove(bereq.url);
          set bereq.url = regsuball(urlencode(urldecode(bereq.url.path)), {"%2F"}, "/");
          if (bereq.url.basename == ""){
            set bereq.url = bereq.url "index.html";
          }
          set var.dateStamp = strftime({"%Y%m%d"}, now);
          set var.canonicalHeaders = ""
            "host:" bereq.http.host LF
            "x-amz-date:" bereq.http.x-amz-date LF
          ;
          set var.canonicalQuery = "";
          set var.signedHeaders = "host;x-amz-date";
          set var.canonicalRequest = ""
            "GET" LF
            bereq.url.path LF
            var.canonicalQuery LF
            var.canonicalHeaders LF
            var.signedHeaders LF
            digest.hash_sha256("")
          ;
                  
          set var.scope = var.dateStamp "/" var.fjvRegion "/s3/aws4_request";
                  
          set var.stringToSign = ""
            "AWS4-HMAC-SHA256" LF
            bereq.http.x-amz-date LF
            var.scope LF
            regsub(digest.hash_sha256(var.canonicalRequest),"^0x", "")
          ;
                  
          set var.signature = digest.fjvv4_hmac(
            var.fjvSecretKey,
            var.dateStamp,
            var.fjvRegion,
            "s3",
            var.stringToSign
          );
                  
          set bereq.http.Authorization = "AWS4-HMAC-SHA256 "
            "Credential=" var.fjvAccessKey "/" var.scope ", "
            "SignedHeaders=" var.signedHeaders ", "
            "Signature=" + regsub(var.signature,"^0x", "")
          ;
          unset bereq.http.Accept;
          unset bereq.http.Accept-Language;
          unset bereq.http.User-Agent;
          unset bereq.http.Fastly-Client-IP;
                    
        }
      • 削除しなくても問題ありませんが、通常のWebサーバーと同じ応答とするために不要なヘッダを削除するスクリプトも追加します。
        再度「Create snippet」をクリックし、作成画面に入ります。
        second vcl snippets
      • 「Name」に適切な名前(remove-header等)を付与し、「within subroutine」を選択、ドロップダウンから「fetch(vcl_fetch)」を選択します。
        second vcl snippets 2
      • 以下のコードを入力し、画面下の「Create」をクリックして保存します。
        unset beresp.http.x-amz-id-2;
        unset beresp.http.x-amz-request-id;
        unset beresp.http.x-amz-delete-marker;
        unset beresp.http.x-amz-version-id;
        unset beresp.http.x-ntap-sg-trace-id;
        unset beresp.http.ETag;
        unset beresp.http.x-amz-server-side-encryption;
        unset beresp.http.x-amz-expiration;
        unset beresp.http.x-amz-mp-parts-count;
        unset beresp.http.X-Fcx-Endpoint-Request;
  4. TLS/SSL証明書の設定
    HTTPSを利用するためにTLS/SSL証明書を設定します。Fastly TLSを利用すると自動的に証明書を取得、ローテーションできます。 サービスの詳細は、Fastly社のドキュメントを参照してください。
    作成時は、手順3で作成したドメインと合わせてください。
  5. DNS設定
    Webサイトのドメイン名をFastlyのエッジサーバーに向けるようにDNSへCNAMEレコードを設定します。
    CNAMEで設定するドメインの確認方法は、Fastly社のドキュメント を確認してください。
    以下は、DNSサービスで設定したときのイメージ画面になります。
    DNS config
  6. CDNサービスの有効化
    手順3の画面に戻り、画面右上の「Activate」から「Production」を選択し、設定を有効化します。
    image
  7. 動作確認
    設定したドメインにアクセスし、ページが表示されるか確認します。
    your-domain.com

まとめ

Fastlyとオブジェクトストレージを組み合わせることで、Webサーバーの構築をせずに静的サイトの配信ができました。
今回は手動で数ファイルをアップロードしましたが、WordPressで出力先をS3にするプラグインを利用すると、WordPressで管理しつつ配信は今回の方法で実施できます。
ぜひ、このデモを参考にWebサーバーの構築なしにWebサイトを公開してみてください。

注意事項

  • 本記事は、実現性の確認を目的としています。実際に利用する場合は実作業前にお客様にて検証および動作確認を実施し、問題なく移行できるか確認してください。
  • 本記事の掲載時点の情報になります。最新の情報は各サービスのサービスページ、技術仕様ページを参照してください。
  • 本記事に記載されている会社名、製品名等の固有名詞は各社の商号、登録商標または商標です。
  • 本記事の他社サイトへのリンクにつきまして、リンク切れの際はご容赦ください。
PageTop