Why are these HTML column headers misaligned?

This is not a solution to your problem with fixed widths, but a step toward your desire for

a sticky header, with dynamic column widths (no declared table width), over scrollable rows, with precise header and column alignment, using just pure CSS/HTML.

I believe it's faster for large tables to be rendered with dynamic column widths, header/column alignment is then guaranteed, plus the code is no longer cluttered with magic numbers. Adding scrolling requires using an enclosing <div> like the snippet below.

UPDATE: Per your comment request, here's a dynamic table (no declared column widths) with a scrolling div that automatically repositions the scrollbar as necessary. Click the "Refill table" button to generate test tables.

const SCROLLBARWIDTH = 16;

let maxWidth = 5; // initial max width of our dummy strings

function dummyString() {
  return 'x'.repeat(Math.floor((Math.random() * maxWidth)+1)); // dummy data that will tend to widen with each refill
}

function refill() {
  document.getElementById("theBody").innerHTML = '<tr></tr>'; // remove any previous table body
  let tableRef = document.getElementById("theTable"); // our scrollable HTML table that we'll add rows to
  for (let i = 0; i < 20; i++) { // create 20 rows of dummy data
    let newRow = tableRef.insertRow(-1); // add a new row to the table, then set the 6 <td> cells...
    newRow.insertCell(-1).appendChild(document.createTextNode(dummyString())); // price
    newRow.insertCell(-1).appendChild(document.createTextNode(dummyString())); // drops
    newRow.insertCell(-1).appendChild(document.createTextNode(dummyString())); // description
    newRow.insertCell(-1).appendChild(document.createTextNode(dummyString())); // category
    newRow.insertCell(-1).appendChild(document.createTextNode(dummyString())); // item #
    newRow.insertCell(-1).appendChild(document.createTextNode(dummyString())); // posted
    }
  let tableWidth = window.getComputedStyle(tableRef).getPropertyValue('width'); // get resulting dynamic table width
  document.getElementById("container").style.width = parseInt(tableWidth) + 1 + SCROLLBARWIDTH + 'px'; // make scrolling div wide enough
  maxWidth++; // so our next refill will tend to have wider dummy strings
}

refill();
#container { overflow-y: scroll; height: 200px; } /* width to be determined after table filled */

thead th { position: sticky; top: 0; }

table { font: 12px Courier; border-collapse: collapse; }

th { background: black; color: white; } /* hide scrolling behind the header */

table, th, td { border: 1px solid black; padding: 5px; }
<button onclick="refill()">Refill table</button> <!-- click to generate wider table -->

<div id="container">
<table id="theTable">
  <thead>
    <tr>
      <th>Price</th>
      <th>Drops</th>
      <th>Description</th>
      <th>Category</th>
      <th>Item #</th>
      <th>Posted</th>
    </tr>
  </thead>
  <tbody id="theBody">
    <tr>
    </tr>
  </tbody>
</table>
</div>

I would suggest using min, max-width instead of width. Also, you added all the column width, but every column has a padding of 5px, which adds 5px on left and 5px on right. So, make sure you add them up.

Here is the fix:

        table {
            /* width: 688px; 688 = column widths 80 + 56 + 280 + 120 + 56 + 96 + 60(padding)*/
            min-width: 748px;
            max-width: 763px;
            table-layout: fixed;
            font: 12px Courier;
            border-collapse: collapse;
        }

        table,
        th,
        td {
            border: 1px solid black;
            padding: 5px;
        }

        tbody {
            display: block;
            /* max-width: 763px; */
            /* 763 = 688 table width + 60(padding) + 15 extra for scrollbar */
            overflow-y: scroll;
            height: 65px;
        }

        thead tr {
            display: block;
        }

        tr:nth-child(even) {
            background-color: #eee;
        }

        /* th:nth-child(1), td:nth-child(1) {text-align: right; width:  80px; }
        th:nth-child(2), td:nth-child(2) {text-align: left;  width:  56px; }
        th:nth-child(3), td:nth-child(3) {text-align: left;  width: 280px; }
        th:nth-child(4), td:nth-child(4) {text-align: left;  width: 120px; }
        th:nth-child(5), td:nth-child(5) {text-align: right; width:  56px; }
        th:nth-child(6), td:nth-child(6) {text-align: left;  width:  96px; } */

        th:nth-child(1),
        td:nth-child(1) {
            text-align: right;
            min-width: 80px;
            max-width: 80px;
        }

        th:nth-child(2),
        td:nth-child(2) {
            text-align: left;
            min-width: 56px;
            max-width: 56px;
        }

        th:nth-child(3),
        td:nth-child(3) {
            text-align: left;
            min-width: 280px;
            max-width: 280px;
        }

        th:nth-child(4),
        td:nth-child(4) {
            text-align: left;
            min-width: 120px;
            max-width: 120px;
        }

        th:nth-child(5),
        td:nth-child(5) {
            text-align: right;
            min-width: 56px;
            max-width: 56px;
        }

        th:nth-child(6),
        td:nth-child(6) {
            text-align: left;
            min-width: 96px;
            max-width: 96px;
        }

        .lastColumn {
            min-width: 114px !important;
        }
    <table>
        <thead>
            <tr>
                <th>Price</th>
                <th>Drops</th>
                <th>Description</th>
                <th>Category</th>
                <th>Item #</th>
                <th class="lastColumn">Posted</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>$5.00
                <td>Oct 10
                <td>Valuable item
                <td>Miscellaneous
                <td>1234
                <td>Sep 10 2020
            </tr>
            <tr>
                <td>$5.00
                <td>Oct 10
                <td>Valuable item
                <td>Miscellaneous
                <td>1234
                <td>Sep 10 2020
            </tr>
            <tr>
                <td>$5.00
                <td>Oct 10
                <td>Valuable item
                <td>Miscellaneous
                <td>1234
                <td>Sep 10 2020
            </tr>
            <tr>
                <td>$5.00
                <td>Oct 10
                <td>Valuable item
                <td>Miscellaneous
                <td>1234
                <td>Sep 10 2020
            </tr>
        </tbody>
    </table>

You just need the last column to take the remaining width. So, specify to that column.