Part of the Accessibility audit
Check your data tables for missing headers
A data table without header cells is a grid of numbers and words with no context. SiteCurl checks every table on your pages for th elements.
No signup required. Results in under 60 seconds.
What this check does
SiteCurl finds every data table on your pages and checks for <th> (table header) elements. Tables with role='presentation' or role='none' are skipped because they are layout tables, not data tables.
The check reports how many data tables are on the page and how many are missing header cells. A table with at least one <th> element passes. A data table with only <td> cells and no headers is flagged.
Header cells give meaning to the data in each column or row. Without them, a screen reader user hears raw values ('42', 'pending', '$299') with no way to know what those values represent.
How this shows up in the real world
When a screen reader encounters a data table, it enters a special table navigation mode. The user can move between cells with arrow keys. As they move, the reader announces the cell's content along with its column header (and row header if one exists). So instead of hearing just '42,' the user hears 'Quantity: 42.' Instead of 'pending,' they hear 'Status: pending.'
Without header cells, this context disappears. The user hears raw values and has to count columns to figure out what each number means. 'Column 1: 42. Column 2: pending. Column 3: $299.' Column 1 could be anything. The data is there, but the meaning is lost.
This matters most for tables with many columns or rows. A simple two-column table may be guessable. A pricing comparison table with 6 columns and 20 rows is nearly impossible to use without headers.
The scope attribute adds even more clarity. <th scope='col'> tells screen readers this header applies to the column below it. <th scope='row'> marks a row header. For complex tables with nested headers, the scope attribute is what makes them navigable.
Why it matters
Data tables are one of the hardest structures for screen reader users to navigate. Unlike headings and links, tables require understanding the relationship between cells across two dimensions. Header cells are the only way to convey that relationship.
Without headers, every cell in the table is an isolated value. Users cannot tell what column a value belongs to without counting cells from the left edge. On a table with 10 columns, this is impractical. Most users give up and look for the information elsewhere.
Tables are also used heavily on pages that screen reader users need the most: pricing pages, comparison charts, feature lists, schedules, and data dashboards. These are decision-making pages. Making the data inaccessible means screen reader users cannot make informed decisions.
Who this impacts most
SaaS pricing pages with feature comparison tables are a common source of this issue. The table shows plans across the top and features down the side, but the first row uses <td> instead of <th>. Screen reader users cannot tell which plan each column represents.
E-commerce sites with product specification tables need headers. A table listing dimensions, materials, and weights is useless if the column labels are not marked as headers.
Dashboard and reporting pages are often the worst offenders. Data tables generated by JavaScript frameworks sometimes output all cells as <td> without marking the first row as headers. The developer sees headers visually (bold, different background color) but the HTML treats them the same as data cells.
How to fix it
Step 1: Change the first row to th elements. If your table has column headers in the first row, change those cells from <td> to <th>. Example: <th>Name</th><th>Price</th><th>Status</th>.
Step 2: Add scope attributes. Add scope='col' to column headers and scope='row' to row headers. This tells screen readers exactly which cells each header applies to.
Step 3: Use thead, tbody, and tfoot. Wrap your header row in <thead>, data rows in <tbody>, and any summary row in <tfoot>. These elements group table sections and give screen readers more structure to work with.
Step 4: Check JavaScript-generated tables. If your table is built by a framework (React, Vue, Angular), check the rendered HTML, not the component template. Make sure the framework outputs <th> elements for header cells, not styled <td> cells.
Step 5: Mark layout tables correctly. If you use a table for layout (not data), add role='presentation' or role='none' to tell screen readers it is not a data table. Layout tables should not have header cells.
Common mistakes when fixing this
Styling td cells to look like headers. Bold text and a gray background make cells look like headers visually, but screen readers still treat them as data cells. Use <th> elements, not styled <td> elements.
Missing scope attributes. Without scope, screen readers may guess which direction a header applies. The guess is usually right for simple tables but fails for tables with both row and column headers. Always add scope to be explicit.
Using th for visual emphasis on non-header cells. Some developers use <th> to make a cell bold. This marks the cell as a header, confusing screen readers about the table structure. Use CSS for visual emphasis on data cells.
Forgetting headers in responsive tables. Tables that collapse into card layouts on mobile may lose their header cells in the mobile view. Make sure the mobile version still conveys which value belongs to which field.
How to verify the fix
After adding headers, run another SiteCurl scan. Tables with <th> elements will pass. For a manual check, right-click the table in your browser, select 'Inspect,' and look for <th> elements in the first row. If you only see <td>, the headers are missing.
To test with a screen reader, navigate into the table and move between cells with arrow keys. Each cell should be announced with its column header. If you hear just the cell value with no header context, the fix is not complete.
The bottom line
Data tables need header cells. Change the first row from td to th, add scope attributes, and wrap rows in thead and tbody. Without headers, screen reader users hear raw values with no way to know what they mean.
Example findings from a scan
Pricing comparison table missing th elements
1 of 2 data tables has no header cells on /features
Product specs table using td for all cells
Related checks
Frequently asked questions
What is the difference between th and td?
th marks a table header cell. td marks a table data cell. Screen readers use th elements to label the data in each column or row. Without th, the data has no context.
What is the scope attribute?
The scope attribute on a th element tells screen readers whether the header applies to a column (scope='col') or a row (scope='row'). It removes ambiguity about which cells the header describes.
Do layout tables need headers?
No. Tables used for layout (not data) should have role='presentation' or role='none' and no header cells. This tells screen readers to treat the table as a visual structure, not a data grid.
Can I check table headers without signing up?
Yes. The free audit checks table headers in a full seven-category scan. No signup needed.
Check your table headers now