According to the docs:
If the source table contains 0 rows, then the COPY operation does not
unload an empty data file; not even a file containing the table column
headings.
And there doesn't seem to be a way to change this behavior.
However, before writing the table to S3, you could write the headers (as data) by querying the INFORMATION_SCHEMA. Then write your table as usual (with OVERWRITE=TRUE) -- and if no rows are written, the header file will remain.
UPDATE
Here's an how to generate a header:
select listagg(column_name,',') within group (order by ordinal_position)
from MY_DB.information_schema.columns
where table_schema='MY_SCHEMA' and table_name='MY_TABLE';
You can then write it as data to a CSV file like this:
copy into @my_stage/my_table (
select listagg(column_name,',')
within group (order by ordinal_position)
from MY_DB.information_schema.columns
where table_schema='MY_SCHEMA' and table_name='MY_TABLE';
)
file_format = (type=csv compression=none field_delimiter=none)
header=false;