Ubuntu✖️Nginx✖️Next.js(SSG)✖️StrapiでWEBサイトの環境を作る
最近、WordPress以外のCMSへの移行の需要も広がりつつあるというお話を聞きます。
CMSとして80%の圧倒的なシェアを持つWordPressですが、
シェアの大きさゆえに攻撃の対象にもなりやすく、
実際、攻撃を受けてセキュリティの被害に遭うことも少なくありません。
また、多くの機能を実装できるため、実装次第で重くなりやすい特徴もあります。
そういったこともあり、WordPressを使わないCMSへの移行を望む企業さんも増えているようです。
その移行先として注目されているのが、ヘッドレスCMSと呼ばれる種類のCMSです。
WordPressでは、コンテンツの管理とサイトのレンダリングを両方行います。
一方、ヘッドレスCMSでは、コンテンツの管理のみを行い、サイトのレンダリングやジェネレートなどは別のサービスを利用して行います。
そのため、サイトからコンテンツへの攻撃がされにくくなります。
また、コンテンツの数が多くなってもサイトのレンダリングに直接影響しないため、表示速度を上げやすいメリットもあります。
この記事でご紹介する「Strapi」は、Node.jsのヘッドレスCMSとしては人気があります。
ヘッドレスCMSは多くのサービスが提供されていて、CMS選びに迷いますが、
ホスティングサービスを利用しなければ無料で利用でき、
Nodejsの環境があればローカルで始められるので、開発のハードルが低いのが大きな理由でしょうか。
Strapiはコンテントタイプ(WordPressでいう投稿タイプやタクソノミー)フィールドを自由に構成でき、
ユーザのロールなども管理でき、APIもRestAPIとGraphQLから選ぶことができるので、
高い拡張性が特徴です。
この記事では、Strapiでサイトを構築する1つの方法として、
OSをUbuntu、WebサーバをNginx、サイトのジェネレートをNext.js、そしてCMSをStrapiで開発を始める方法を共有します。
ここでは動作するまでのインストールに重点を置き、コンテンツの作成などは省略します。
目次
UbuntuでNginxによるサーバを設定する
Nginxのインストール
参考にさせていただいたサイト: 【Ubuntu Server 22.04】Nginx公式サイトのリポジトリを使用してインストール | VPS Life
↑詳細はこちらのサイトが詳しいです。
この記事ではざっくりと流れだけ触れていきます。
パッケージのインストール
1$ sudo apt update 2$ sudo apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring
署名鍵のインポートと確認
1$ curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \ 2 | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null 3$ gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg
正しくインポートされると以下が表示されます
1pub rsa2048 2011-08-19 [SC] [expires: 2024-06-14] 2 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 3uid nginx signing key
リポジトリの追加とインストール
1$ echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \ 2http://nginx.org/packages/mainline/ubuntu `lsb_release -cs` nginx" \ 3 | sudo tee /etc/apt/sources.list.d/nginx.list 4$ sudo apt update 5$ sudo apt install nginx
インストールの確認
nginxのインストールに成功すると、以下のコマンドでバージョンを確認できます。
1$ nginx -v
ufwによるファイアウォール設定とNginxでサーバの設定
ufw によるファイアウォール設定
そのままサーバを開設すると、セキュリティ的に良くないので、ufwを使って開放するポートを制限します。
参考にさせていただいたサイト: ufwの最小限設定について – とりあえずノートがわりに書いてます
ufwを設定するときは、いきなり実行するとSSH接続ができなくなってしまうので、
まずは、ufw status
を実行して、Status: inactive
であることを確認します。
確認できたら、以下のコマンドを 1つずつ実行して、許可するポートを制限します。
1$ ufw allow 22 2$ ufw allow http 3$ ufw allow https 4$ ufw allow domain
そしてufwを有効化。
1ufw enable
最後に ufw status
を実行して、目的のポートが開放されていることを確認します。
1$ ufw status 2 Status: active 3 4To Action From 5 -- ------ ---- 6 22 ALLOW Anywhere 7 53 ALLOW Anywhere 8 22 (v6) ALLOW Anywhere (v6) 9 53 (v6) ALLOW Anywhere (v6)
利用するVPSなどによっては、VPSのコントロールパネルなどで、
別途、ポートの開放が必要です。
Nginxでサーバの設定
ここでご紹介する方法では、Next.jsでジェネレートしたファイルを静的にNginxでサーブし、
それとは別にStrapiでCMSをサーブします。
そのため、静的にサーブするドメインとCMSをサーブするドメインを別々に割り当てます。
Nginxで静的なサーブ
/etc/nginx/nginx.conf を設定して
1server { 2 server_name <サイト用のドメイン名>; 3 4 location / { 5 root /var/www/<Next.jsのディレクトリ>/out; 6 7 index index.html index.htm; 8 } 9 10 error_page 404 /404.html; 11 12 # redirect server error pages to the static page /50x.html 13 # 14 error_page 500 502 503 504 /50x.html; 15 location = /50x.html { 16 root /usr/share/nginx/html; 17 } 18 19}
Strapi用の設定
/etc/nginx/conf.d/default.conf のhttpの設定で以下を設定
1http { 2 ︙ 3 include /etc/nginx/conf.d/*.conf; 4 include /etc/nginx/sites-enabled/*; 5}
/etc/nginx/sites-enabled/<Strapi用のドメイン名>
1server { 2 3 listen 80; 4 server_name <Strapi用のドメイン名>; 5 6 # Proxy Config 7 location / { 8 proxy_pass http://127.0.0.1:1337; 9 proxy_http_version 1.1; 10 proxy_set_header X-Forwarded-Host $host; 11 proxy_set_header X-Forwarded-Server $host; 12 proxy_set_header X-Real-IP $remote_addr; 13 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 14 proxy_set_header X-Forwarded-Proto $scheme; 15 proxy_set_header Host $http_host; 16 proxy_set_header Upgrade $http_upgrade; 17 proxy_set_header Connection "Upgrade"; 18 proxy_pass_request_headers on; 19 add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive"; 20 } 21}
あとは、sudo nginx -t
でNginxの設定に問題がないことを確認し、sudo systemctl start nginx
でNginxを起動すれば、
Nginxの設定は完了です。
さらにUbuntu上でLet's EncryptによりSSLを設定する場合は、
↓こちらの記事の方法がとても参考になります(Nginxの設定から更新まで自動!)。
Ubuntu 20.04でLet’s Encryptを使用してNginxを保護する方法 | DigitalOcean
Node.jsでNext.jsとStrapiの環境を準備
Strapi公式のガイド: https://docs.strapi.io/dev-docs/integrations/next-js
Next.jsのインストール
1$ npx create-next-app@latest
でNext.jsをインストールします。
Strapiのインストール
ここでは、Next.jsをインストールした場所にstrapiをインストールしてみます。 StrapiをNext.jsと別の場所に置く場合などは、同じ場所にインストールしない方が良いでしょう。
1$ cd <Next.jsをインストールしたディレクトリ> 2$ yarn create strapi-app my-project --quickstart
でStrapiをインストールします。
このとき、利用するDBをSQLite、MySQL、MariaDB、PostgreSQLから選択できます。
SQLite以外を利用する場合は、別でMySQLなどインストール・きどうしておく必要があります。
ここではMySQLを使った方法をご紹介します。
MySQLのインストール
参考にさせていただいたサイト: Ubuntu 20.04にMySQLをインストールする方法 | DigitalOcean
インストール
1$ sudo apt update 2$ sudo apt install mysql-server
設定
パスワード
1$ sudo mysql_secure_installation
ここでいくつか質問されるので、
Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG:
では 2、
Re-enter new password:
では新たなパスワード、
Do you wish to continue with the password provided?(Press y|Y for Yes, any other key for No) :
で Y
として、パスワードを設定します。
ユーザ追加と権限の付与
1$ mysql -u root -p
1CREATE USER 'ユーザ名'@'localhost' IDENTIFIED BY 'ユーザ名'; 2GRANT ALL PRIVILEGES ON * . * TO 'ユーザ名'@'localhost'; 3FLUSH PRIVILEGES;
Strapi用DBの作成
1exit
1mysql -u ユーザ名 -p
1CREATE DATABASE strapi;
これでMySQLの準備は完了。
Strapiのディレクトリに既にある.env、なければ新規に.envという名前のファイルを作り、以下のように記述
.env
1︙ 2DATABASE_CLIENT=mysql2 3DATABASE_HOST=127.0.0.1 4DATABASE_PORT=3306 5DATABASE_NAME=strapi 6DATABASE_USERNAME=ユーザ名 7DATABASE_PASSWORD=パスワード 8DATABASE_SSL=false
続いて、./config/database.jsというファイルを開き、以下のように設定します。
1module.exports = ({ env }) => ({ 2 connection: { 3 client: 'mysql', 4 connection: { 5 host: env('DATABASE_HOST', '127.0.0.1'), 6 port: env.int('DATABASE_PORT', 3306), 7 database: env('DATABASE_NAME', 'strapi'), 8 user: env('DATABASE_USERNAME', 'strapi'), 9 password: env('DATABASE_PASSWORD', 'strapi'), 10 ssl: { 11 rejectUnauthorized: env.bool('DATABASE_SSL_SELF', false), // For self-signed certificates 12 }, 13 }, 14 debug: false, 15 }, 16});
Strapiのディレクトリで以下コマンドを実行すると、Strapiが立ち上がり、表示されたURLにアクセスすると、ユーザ登録画面が表示されます。
1yarn develop
Next.jsとStrapiでデータを連携する
SSGでNext.jsからStrapiのAPIを呼び出す
※Strapiでrestaurantというコンテンツタイプを作った場合を例にしています(フィールドはtitleとslugを設定)。
SSGでNext.jsからAPIを呼び出す場合、pages/ のファイルで、getStaticProps()
を使うことで、
ページをジェネレートする前にAPIを呼び出して、静的にHTMLを出力できます。
1import axios from 'axios' 2 3function Page ({ posts, pagination, error } { 4 return ( 5 <section> 6 <h1>ページサンプル</h1> 7 <ul> 8 posts.map(post => ( 9 <li key={post.id}>{post?.attribute?.title}</li> 10 )) 11 </ul> 12 </section> 13 ) 14} 15 16export async function getStaticProps () { 17 try { 18 const res = await axios.get('http://localhost:1337/api/restaurants'); 19 20 return { 21 props: { 22 posts: res?.data, 23 pagination: { 24 ...res?.meta?.pagination 25 } 26 } 27 } 28 } catch (error) { 29 return { error } 30 } 31} 32 33export default Page
ダイナミックルーティングでSSGする
pages/restaurants/[slug]/index.tsx のようにダイナミックルーティングでslugごとのページを生成したい場合、
getStaticPaths
を使います。
1import axios from 'axios'; 2 3function Page ({ post } { 4 return ( 5 <section> 6 <h1>{post?.attribute?.title}</h1> 7 ︙ 8 </section> 9 ) 10} 11 12export async function getStaticPaths () { 13 let paths: (string | { 14 params: ParsedUrlQuery; 15 locale?: string | undefined; 16 })[] = [] 17 const res = await axios.get(`http://localhost:1337/api/restaurants?pagination[pageSize]=10000&populate=slug`) 18 if (res?.data?.length > 0) { 19 paths = res?.data?.map(post => ({ 20 params: { 21 slug: post?.attributes?.slug ?? null 22 } 23 })) 24 } 25 return { paths, fallback: false } 26} 27 28export async function getStaticProps ({ params }) { 29 const res = await axios.get(`http://localhost:1337/api/restaurants?filters\[slug\][$eq]=${ params?.slug }`); 30 31 return { 32 props: { 33 post: res?.data?.length > 0 ? res?.data[0] : undefined, 34 } 35 } 36} 37 38export default Page
対応してみた感想
WordPressに比べると、事前の準備も多く必要で、慣れるまでは大変かもしれません。
SSGの場合、記事が多ければ多いほどビルドまでに時間がかかる問題もあります。
ただ、基本的には1つの言語でバックエンドからフロントエンドまで調整することが可能で、
SSGが故にクライアント側の表示は高速で、ビルドするとき以外はサーバの負担も軽くすみます。
Strapiはコンテンツも自由に設定でき、実際に拡張性は高いと感じました。
また、無料で試せるので、ヘッドレスCMSの練習としてもいいと思いました。
2023.8.28 追記
数百ページを超えるようなサイトでは、記事を更新するたびに全ページをbuildするSSGは向きません。
その場合、Next.jsではISR(Incremental Static Regenerate)という仕組みが用意されていて、設定を工夫することで大量のページを含むサイトにも対応できるので、そちらの利用のご検討をおすすめします。
Author Profile
NINOMIYA
Webデザイナー兼コーダー出身のフロントエンド開発者です。 UXデザインやチーム開発の効率化など、勉強中です。
SHARE