HTML word wrap in a table does not work the way it does elsewhere, because a table sizes its columns from their content before any wrapping rule is considered.

The default is table-layout: auto. The browser reads every cell, finds the widest content in each column, and allocates width accordingly.
So a single long file path in row 40 decides the width of that column, and squeezes everything else.
The fix is two declarations
table { table-layout: fixed; width: 100%; }
fixed tells the browser to stop measuring content. Column widths come from the first row, or from a <colgroup>, and cells wrap to fit.
Now the wrapping rules you write on cells actually apply.
Declaring column widths properly
Put the widths in a colgroup rather than on individual cells. It keeps the sizing in one place and is read before any row.
<table>
<colgroup>
<col style="width: 18%">
<col style="width: 46%">
<col style="width: 18%">
<col style="width: 18%">
</colgroup>
<thead>
<tr><th>File</th><th>Note</th><th>Owner</th><th>Updated</th></tr>
</thead>
<tbody>
<tr><td>report.html</td><td>Rebuilt after the schema change</td><td>Sam</td><td>2026-09-16</td></tr>
</tbody>
</table>
Percentages keep the table usable at any width. Pixel widths plus table-layout: fixed produce a table that overflows on a phone.

Wrapping rules per column
Once the layout is fixed, decide per column what should happen to content that does not fit.
| Column type | Rule | Result |
|---|---|---|
| Descriptions, notes | overflow-wrap: anywhere |
Wraps, including long URLs |
| File paths, ids | nowrap + text-overflow: ellipsis |
One line, cut off with a title |
| Dates, amounts | white-space: nowrap |
Never splits across lines |
| Code snippets | white-space: pre-wrap |
Keeps spacing, still wraps |
| Dense hashes in a narrow column | word-break: break-all |
Fills the column, breaks anywhere |
The CSS:
td.note { overflow-wrap: anywhere; }
td.path {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
td.date { white-space: nowrap; }
text-overflow: ellipsis needs overflow: hidden and white-space: nowrap alongside it. Any one alone does nothing.
When you truncate, keep the value recoverable:
<td class="path" title="/var/log/application/error.log">/var/log/application/error.log</td>
The title shows on hover and is read by assistive technology. Without it the truncated value is simply lost.
Dates and numbers that should never split
A date breaking across two lines reads as two separate values. So does a currency amount split after the symbol.
white-space: nowrap on those columns costs nothing and removes a recurring irritation. It is one of the few places where turning wrapping off is the right choice.
For a value with an internal space you want to keep together, a non breaking space in the markup works too. 12 GB will not split.
Wide tables on narrow screens
A table with seven columns is not going to fit a phone, and no wrapping rule changes that.
Contain the overflow rather than letting it stretch the page:
.table-scroll { overflow-x: auto; }
<div class="table-scroll">
<table> ... </table>
</div>
The table scrolls sideways inside its own box and the surrounding page stays the width of the screen. Without this the whole layout gains a horizontal scrollbar, which is the symptom described in HTML text not wrapping.
Consider also which columns a phone reader needs. Hiding two of seven in a narrow media query often beats scrolling.

Why the first row matters under fixed layout
Under table-layout: fixed the browser reads widths from the colgroup if there is one, and otherwise from the cells of the first row only.
Every later row is laid out to those widths, whatever it contains. That is what makes the layout predictable.
It also means a width set on a cell in row five is ignored entirely. If a column is the wrong size, look at the colgroup or the first row, not the cell you were staring at.
/* has no effect under fixed layout */
tbody tr:nth-child(5) td:first-child { width: 300px; }
A second consequence: a header row hidden with display: none on a narrow screen takes its widths with it. Hide the header text, not the row.
Content that still overflows the cell
Fixed layout constrains the column, and it does not force the content inside to cooperate. An image or a <pre> block wider than the column will still spill.
td img { max-width: 100%; height: auto; }
td pre { white-space: pre-wrap; overflow-wrap: anywhere; }
Both are worth setting once for every table rather than per case. They are the table equivalent of the flex min-width: 0 rule.
Header cells
Headers are usually short labels, and a header wrapping to two lines while its neighbours do not makes a ragged row.
Set a consistent alignment and let them wrap rather than forcing nowrap, which reintroduces the width problem you just fixed.
th { vertical-align: bottom; text-align: left; overflow-wrap: anywhere; }
Checklist
table-layout: fixedandwidth: 100%on the table.- Widths declared in a
colgroup, in percentages. - A wrapping rule chosen per column rather than globally.
- Truncated cells carry a
titlewith the full value. - Dates and amounts set to
nowrap. - The table wrapped in an
overflow-x: autocontainer.
HTML word break compares the wrapping properties in more detail, including when break-all is the right choice.
Editing and sending the table
Tables are the thing people most often need to correct after sending. A number changes and the file has to be rebuilt and resent.
Paste the table into a NOS document and it renders as written at an address of its own, with the text clickable so a value can be corrected in place.
Turning HTML into a link is that step, and the link you already sent points at the corrected version.
If the table needs to stay editable by other people, editable HTML tables and table to link cover that path.