CSSで、中身が少なくてもフッターを画面の下に置く方法
中身が少ないページでフッターが画面の途中に浮いてしまうときは、body を「画面の高さ以上」の縦並びの flex にして、main に flex: 1 を付けます。main が余った高さを受け取って伸びるので、フッターは画面の下に来ます。中身が画面より多いときは、フッターは本文の後ろに続き、本文に重なりません。
検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。HTML と CSS のページを Playwright で開き、主に幅390px・高さ700pxの画面で、getBoundingClientRect() で要素の位置を測りました。Firefox と Safari、アドレスバーが伸び縮みするスマートフォンの実機では試していません。

コード全体
HTML は、body の直下に header、main、footer を並べます。
<body>
<header>サイト名</header>
<main>
<p>本文の段落 1。中身が少ないページでも、フッターが画面の下に来るかを確かめます。</p>
</main>
<footer>フッター</footer>
</body>
CSS は次のとおりです。色や余白などの見た目の CSS は、配置に関わらないので省いています。下で示す高さの数値は、省いた見た目の CSS(ヘッダーなどの padding や行の高さ)も入れたページで測ったものです。
body {
margin: 0;
min-height: 100vh;
min-height: 100dvh;
display: flex;
flex-direction: column;
}
main {
flex: 1;
}
display: flex と flex-direction: column で、body の中の3つの要素を縦に並べます。min-height で、body の高さを画面の高さ以上にします。
main の flex: 1 は、flex-grow: 1 を含む一括指定です。flex-grow は、並べた要素の合計より入れ物が大きいときに、余った長さをどれだけ受け取るかを決めます。header と footer は初期値の0のままなので、余った高さはすべて main に入ります。
画面の大きさを幅390px・高さ700pxにして、段落が1つのページで測りました。
| 要素 | 上端 | 下端 | 高さ |
|---|---|---|---|
header |
0px | 51.2px | 51.2px |
main |
51.2px | 648.8px | 597.6px |
footer |
648.8px | 700px | 51.2px |
footer の下端が、画面の高さと同じ700pxになりました。ページ全体の高さも700pxで、スクロールは出ません。画面の高さを500pxと900pxに変えても、footer の下端はそれぞれ500pxと900pxでした。main を flex-grow: 1 だけにしても、位置は同じでした。
段落を20個にすると、ページ全体の高さは1542pxになり、footer は最後の段落の後ろ(上端1490.7px)に来ました。いちばん下までスクロールしたとき、最後の段落の下端は画面の616.7px、footer の上端は648.7pxで、段落はフッターに隠れていません。
ボタンを押して段落を3つずつ足し、ページの高さの変わり方を見たのが、次の画像です。

min-height と 100dvh を2行に分けて書く理由
body の高さは、min-height で「最低でも画面の高さ」にします。段落20個のページで min-height を height に書きかえると、フッターの位置は変わりませんでしたが、body の高さは700pxで止まりました。700pxより下の main と、footer(1490.7px〜1541.9px)は、body の外へはみ出していました。中身に合わせて body が伸びるのは、min-height のほうです。
dvh は、スマートフォンのアドレスバーなどが出たり引っ込んだりするのに合わせて変わる単位です(長さの単位の説明(ビューポートに基づく単位))。1行目の 100vh は、dvh が分からないブラウザのために置いています。CSS のエラー処理の説明にあるとおり、ブラウザは分からない値の宣言を無視するので、その場合は1行目が残ります。2行目を存在しない単位の 100xvh に書きかえて試すと、body の min-height は1行目の値の700pxになり、footer の下端も700pxでした。
MDN の互換性データ(@mdn/browser-compat-data 8.1.8)では、dvh などの単位は Chrome 108、Firefox 101、Safari 15.4(iOS の Safari も 15.4)から使えます。
body の margin を 0 にする
margin: 0 を消すと、中身が画面に収まるページでも、スクロールが出ます。
HTML の仕様の、描画の章にある「The page」の項のとおり、body には既定で8pxの margin が付きます。body の高さが画面と同じ700pxでも、上下に8pxずつ足されるので、ページ全体の高さは716pxになりました。footer の下端は708pxで、画面の下から8pxはみ出し、16pxだけスクロールできる状態でした。
footer の margin-top: auto でも、フッターは下に来る
main に flex: 1 を付ける代わりに、footer に margin-top: auto を付けても、フッターは画面の下に来ます。flex の中では、auto の margin が余った長さを受け取るためです(flexbox の配置の説明の、auto マージンの節)。
footer {
margin-top: auto;
}
段落が1つのページで、footer の位置は648.8px〜700pxで、main { flex: 1 } と同じでした。違うのは main の高さです。flex: 1 では main が597.6pxまで伸びましたが、margin-top: auto では main は中身の高さの102.4pxのままで、余りは main と footer の間のすき間になりました。
grid で書くときは、行の数を子要素に合わせる
grid でも同じ配置にできます。grid-template-rows で、auto、1fr、auto の3行を作ります。1fr の行が、残りの高さを受け取ります。
body {
margin: 0;
min-height: 100vh;
min-height: 100dvh;
display: grid;
grid-template-rows: auto 1fr auto;
}
段落が1つのときも20個のときも、header・main・footer の位置は、flex で書いたときと同じでした。
ただし、grid の行は、子要素に上から順に割り当てられます。header と main の間に nav を足すと、2行目の 1fr を nav が受け取り、nav の高さが495.3pxになりました。main は546.4pxから始まり、メニューと本文の間に大きなすき間が空きます。フッターの下端は700pxのままなので、フッターだけを見ていると気づきにくい崩れ方です。
| 書き方 | nav の高さ |
main の上端〜下端 |
|---|---|---|
grid(auto 1fr auto) |
495.3px | 546.4px〜648.8px |
grid(auto auto 1fr auto) |
43.2px | 94.4px〜648.8px |
flex(main { flex: 1 }) |
43.2px | 94.4px〜648.8px |
grid-template-rows を auto auto 1fr auto にして、行を子要素の数に合わせると、main が伸びるようになりました。flex の書き方は、flex: 1 を main に付けているので、nav を足しても書きかえずに main が伸びました。
ページ全体を div で包んでいるときは、その div に書く
header から footer までを <div class="page"> のような要素で包んでいるページでは、body に書いても効きません。上の CSS を body に書いたまま包むと、body の高さは700pxになりましたが、footer の下端は204.8pxで、本文のすぐ下に来ました。body の中で並ぶのは div.page 1つだけで、main は flex で並べられる要素ではなくなるためです。
このときは、包んでいる要素に同じことを書きます。
body {
margin: 0;
}
.page {
min-height: 100vh;
min-height: 100dvh;
display: flex;
flex-direction: column;
}
.page > main {
flex: 1;
}
この CSS では、段落が1つのとき footer は648.8px〜700pxに来ました。段落が20個のときは、body に書いたときと同じく、最後の段落の後ろに続きました。
position: fixed で下に置くと、本文に重なる
footer を position: fixed で画面の下に固定しても、フッターは画面の下に来ます。
body {
margin: 0;
}
footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
}
ただし、これはスクロールしてもずっと画面の下に付いたままのフッターで、本文の上に重なります。
段落20個のページでいちばん下までスクロールすると、最後の段落の下端は画面の667.7px、footer の上端は648.8pxで、段落の下の18.9pxがフッターに隠れていました。中身が少ないときだけ下に置き、多いときは本文の後ろに続けたいなら、ここまでの flex か grid の書き方を使います。





