posts
git pushしたらデザインが消えた。CSSが404になった原因はフォルダの権限だった
トップページのデザインを作り直して git push したら、サイトのデザインが丸ごと消えて、文字だけの画面になりました。キャッシュを疑い、Nginxの設定を疑い、設定ファイルを壊しかけ、最終的にたどり着いた原因はフォルダの読み取り権限でした。
先に結論を書いておきます。
- GitHub Actionsで送ったフォルダが、持ち主しか入れない状態(
drwx------)になっていて、NginxがCSSを読めなかった - 本当は「読めない」のに、Nginxは 404(見つからない) を返していたので、原因の見当がつかなかった
- 持ち主と権限を直し(
chownとchmod)、Actionsの手順書に「必ず権限を直すステップ」を入れて再発を防いだ
同じように「ファイルはあるはずなのに404」で困っている人の参考になればと思います。
症状:文字だけのサイトになった
このブログは、番外編2の手順で、git push するとGitHub Actionsが自動でサーバーに公開する仕組みにしています。
トップページの見出しやデザインを作り直して git push したところ、まずActionsの画面で、転送のステップが失敗(赤い×)になっていました。そこを直しているうちに、サイトのほうもおかしいことに気づきました。
- 新しく作った見出しやデザインが反映されていない
- ブラウザの再読み込み(
Ctrl + Shift + R)をしても変わらない - シークレットウィンドウで開くと、CSSがまったく効いていない、文字だけの画面になる

シークレットウィンドウでも同じなので、自分のブラウザのキャッシュの問題ではありません。サーバーがCSSを返していない、ということです。
遠回り1:Nginxの設定を疑った
CSSのファイルを直接取りに行って確かめました。PowerShellで打ちます。
curl.exe -I https://noluckblog.com/css/style.css
返ってきたのは 404 Not Found でした。「CSSのファイルが無い」と言われています。
手元の public フォルダにはCSSがありますし、Actionsで送ったはずです。そこで「Nginxが正しい場所を見ていないのでは」と考えて、Nginxの設定ファイルを開き、CSSのための設定を書き足してみました。
コピペした設定に、リンクの書き方が混ざった
ここでさらにハマりました。書き足す設定を、チャットの画面からコピーしてサーバーに貼ったところ、server_name の行がこんな形で貼られていたのです。
server_name noluckblog.com [www.noluckblog.com](http://www.noluckblog.com);
チャットの画面が、www. で始まる文字を自動でリンクにしていて、コピーするとMarkdownのリンクの書き方([表示する文字](URL))ごと持ってきてしまっていました。当然、nginx -t は通りません。何度コピーし直しても同じ形で貼られてしまうので、最終的には sed コマンドで、この形の部分をまとめて元の文字に置き換えて直しました。
nanoの「.save」ファイルも残っていた
もう1つ、nginx -t が通らない原因がありました。nanoで編集している途中で画面が閉じたせいで、sites-enabled の中に yourlog.save というファイルが残っていたのです。Nginxは sites-enabled の中のファイルを全部設定として読むので、中身が同じ設定が2つあることになり、エラーになっていました。このファイルを消して、ようやく nginx -t が test is successful に戻りました。
ところが、設定を直して反映しても、CSSは404のまま。何も変わりませんでした。
原因:フォルダが「持ち主しか入れない」状態だった
設定ではない、となると、ファイルそのものを疑うしかありません。サーバーに入って、公開フォルダの中身を見ました。
ls -la /var/www/yourlog/public
こんな行が並んでいました。
drwx------ 2 1001 1001 4096 (日付) css
drwx------ … 1001 1001 … (日付) posts
さらに、Nginxのエラーの記録(ログ)を見ました。
tail -n 20 /var/log/nginx/error.log
[crit] … "/var/www/yourlog/public/css/style.css" failed (13: Permission denied)
Permission denied(権限がない)。ファイルはちゃんとあったのに、Nginxが読めなかったのです。
drwx------ の意味
ls -la の左端の10文字は、そのファイルやフォルダの権限を表しています。
d rwx --- ---
│ │ │ └ その他の人(Nginxはここ)
│ │ └ グループ
│ └ 持ち主
└ d はフォルダという意味
| 文字 | 意味 |
|---|---|
r |
読める |
w |
書ける |
x |
フォルダの場合は「中に入れる」、ファイルの場合は「実行できる」 |
- |
その権限がない |
drwx------ は、持ち主だけが読み書きでき、中に入れる。それ以外の人は何もできないという状態です。NginxはUbuntuでは www-data というユーザーで動いていて、このフォルダから見ると「その他の人」です。だから css フォルダの中に入れず、style.css を読めませんでした。
持ち主が 1001 という番号になっていたのは、GitHub Actions側で使われているユーザーの番号です。Actionsの転送(appleboy/scp-action)は、ファイルをまとめてサーバーに送って展開する仕組みで、そのときに送り元の持ち主や権限の情報まで一緒に運ばれてしまったようです。
権限がないのに、なぜ404だったのか
ここが一番の遠回りの原因でした。権限がないなら、ふつうは 403 Forbidden(禁止されています) が返ってくるはずです。手動で公開していたころは、実際に403が出ていました。
今回404だったのは、このブログのNginxの設定にこの行があったからです。
location / {
try_files $uri $uri/ =404;
}
try_files は「このファイルがあれば返す。なければ次を試す。全部なければ404」という意味です。Nginxから見ると、読めないファイルは「無い」のと同じ扱いになり、最後の =404 まで進んで404を返していました。
つまり、画面に出ていたのは404でも、本当の原因は権限でした。ステータスコードだけで判断せず、エラーログを見るのが大事だと身にしみました。
直し方
サーバーに入って、次の4つを打ちました。
chown -R root:root /var/www/yourlog
find /var/www/yourlog -type d -exec chmod 755 {} +
find /var/www/yourlog -type f -exec chmod 644 {} +
systemctl reload nginx
| コマンド | 何をしているか |
|---|---|
chown -R root:root /var/www/yourlog |
中身全部の持ち主を、サーバーの root にそろえる |
find … -type d -exec chmod 755 {} + |
フォルダを全部 drwxr-xr-x(誰でも中に入れて読める)にする |
find … -type f -exec chmod 644 {} + |
ファイルを全部 -rw-r--r--(誰でも読める。書けるのは持ち主だけ)にする |
systemctl reload nginx |
Nginxに反映する |
フォルダとファイルで数字を分けているのは、フォルダには「中に入る(x)」が必要で、ファイルには要らないからです。chmod -R 755 で全部まとめて変えても読めるようにはなりますが、ファイルに不要な実行の権限まで付いてしまいます。
もう一度確かめると、
curl.exe -I https://noluckblog.com/css/style.css
200 OK が返ってきて、サイトのデザインも元どおりになりました。
再発防止:Actionsの手順書を直す
もともとActionsの手順書(deploy.yml)には、転送のあとに chmod -R 755 で権限を直すステップを入れていました。それでも直らなかったのは、転送のステップが失敗扱いになると、その後ろのステップは実行されないからです。最初に見た赤い×のとき、権限を直すステップは飛ばされていました。
そこで、最後のステップをこう変えました。
- name: Fix permissions and reload Nginx
if: always()
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_IP }}
username: root
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
chown -R root:root /var/www/yourlog
find /var/www/yourlog -type d -exec chmod 755 {} +
find /var/www/yourlog -type f -exec chmod 644 {} +
systemctl reload nginx
if: always() は「前のステップが成功しても失敗しても、必ず実行する」という指定です。これで、転送がどんな結果になっても、最後に必ず持ち主と権限がそろうようになりました。それ以来、同じ症状は出ていません。
次に同じことが起きたら見る順番
- シークレットウィンドウで開く … 同じならキャッシュではなくサーバー側の問題
curl.exe -IでCSSのURLを直接たたく … 200か、403か、404かtail /var/log/nginx/error.logを見る …Permission deniedなら権限、No such fileなら本当にファイルがないls -laで権限と持ち主を見る … フォルダがdrwxr-xr-x、ファイルが-rw-r--r--になっているか- Nginxの設定は最後 … 書き換えたら必ず
nginx -t。コピペした中身は目で確認する
今回は5から始めてしまったせいで、設定ファイルまで壊しかけました。「設定を変える前に、まずログを見る」を自分のルールにしています。
学んだこと
| つまずいたこと | 原因 | 学んだこと |
|---|---|---|
| CSSが404になった | フォルダが drwx------ で、Nginxが中に入れなかった |
404でも権限が原因のことがある。エラーログを見る |
| 403ではなく404だった | try_files が「読めない」を「無い」として扱った |
表示されたステータスコードだけで判断しない |
nginx -t が通らなくなった |
コピペで [www…](http://www…) が混ざった |
貼った設定は目で確認する |
nginx -t がまだ通らない |
nanoの .save ファイルが sites-enabled に残っていた |
sites-enabled に余計なファイルを置かない |
| 権限を直すステップがあったのに直らなかった | 前のステップが失敗して、飛ばされていた | 後片付けのステップには if: always() を付ける |
ファイルの権限は、手動で公開していたころから何度もつまずいてきたところです。仕組みを一度ちゃんと理解したので、次は数分で直せると思います。
あわせて読みたい
- 【番外編2】GitHub連携編 — git pushだけで公開まで自動化する — この記事で直した
deploy.ymlの全体 - 【第7回】記事の作成編 — Hugoで書いて公開するまで — 手動で公開していたころの403エラーの話
- 【第5回】環境づくり編 — SSH・ufw・Nginx・Hugoを準備する — Nginxの設定ファイルの1行ずつの意味