Они немного отличаются - у ETag нет никакой информации, которую клиент может использовать, чтобы определить, следует ли делать запрос на этот файл снова в будущем. Если ETag - это все, что у него есть, он всегда должен будет сделать запрос. Однако, когда сервер считывает ETag из запроса клиента, сервер может затем определить, следует ли отправить файл (HTTP 200) или сказать клиенту просто использовать их локальную копию (HTTP 304). ETag - это просто контрольная сумма для файла, который семантически изменяется при изменении содержимого файла.
Заголовок Expires используется клиентом (и прокси-серверами / кэшами), чтобы определить, нужно ли ему вообще отправлять запрос серверу. Чем ближе вы находитесь к дате истечения срока действия, тем больше вероятность того, что клиент (или прокси-сервер) сделает HTTP-запрос для этого файла с сервера.
Поэтому на самом деле вы хотите использовать ОБА заголовки - установите для заголовка Expires разумное значение, основанное на частоте изменения содержимого. Затем настройте отправку ETag так, чтобы, когда клиенты отправляли запрос на сервер, он мог легче определить, следует ли отправлять файл обратно.
Последнее замечание об ETag - если вы используете установку сервера с балансировкой нагрузки на нескольких машинах, работающих под управлением Apache, вы, вероятно, захотите отключить генерацию ETag. Это связано с тем, что inode используются как часть алгоритма хеширования ETag, который будет отличаться для разных серверов. Вы можете настроить Apache так, чтобы он не использовал inode в качестве части вычисления, но затем вы должны убедиться, что временные метки в файлах совпадают, чтобы гарантировать, что один и тот же ETag генерируется для всех серверов.