How does Postgres store many varchars on disk?
Your considerations are sound, but all of this is already taken care of automatically by PostgreSQL:


A NULL value takes up no space at all in a PostgreSQL table row.
A varchar will only occupy as much space as the value actually has bytes. The length limit (type modifier) does not waste any space.
For rows that are really wide (2000 bytes and above), the varchars are first compressed and then, if the result is still too large, stored externally in a TOAST table. If you SELECT from such a row, the toasted values are not read unless you specify the column values are requested.

But if the size limit for a row is 850 bytes, that is too small to get in that range.


You can check the estimated average row size in bytes by looking at the width column in the output of EXPLAIN SELECT * FROM tablename.

PostgreSQL v12 introduced the toast_tuple_target storage parameter which would enable you to reduce the limit for TOASTing data. If you tune that, and after rewriting the table with VACUUM (FULL), PostgreSQL can store the data in the way you want to. Then you can see if that actually gets you better performance.