add: add "Vary" header to responses

Co-authored-by: Steve Chavez <stevechavezast@gmail.com>
This commit is contained in:
Andrei Dziahel
2026-03-16 09:55:20 -05:00
committed by Steve Chavez
co-authored by Steve Chavez
parent d5df12c9c2
commit 1f54e2accc
8 changed files with 86 additions and 10 deletions
+3
View File
@@ -13,9 +13,12 @@ All notable changes to this project will be documented in this file. From versio
- Optimize requests with `Prefer: count=exact` that do not use ranges or `db-max-rows` by @laurenceisla in #3957 - Optimize requests with `Prefer: count=exact` that do not use ranges or `db-max-rows` by @laurenceisla in #3957
+ Removed unnecessary double count when building the `Content-Range`. + Removed unnecessary double count when building the `Content-Range`.
- Add config `client_error_verbosity` to customize error verbosity by @taimoorzaeem in #4088, #3980, #3824 - Add config `client_error_verbosity` to customize error verbosity by @taimoorzaeem in #4088, #3980, #3824
- Add `Vary` header to responses by @develop7 in #4609
### Changed ### Changed
- All responses now include a `Vary` header by @develop7 in #4609
- Log error when `db-schemas` config contains schema `pg_catalog` or `information_schema` by @taimoorzaeem in #4359 - Log error when `db-schemas` config contains schema `pg_catalog` or `information_schema` by @taimoorzaeem in #4359
+ Now fails at startup. Prior to this, it failed with `PGRST205` on requests related to these schemas. + Now fails at startup. Prior to this, it failed with `PGRST205` on requests related to these schemas.
+1
View File
@@ -15,6 +15,7 @@ BOM
Bytea Bytea
Cardano Cardano
cd cd
CDNs
centric centric
CLI CLI
CMS CMS
+1
View File
@@ -21,6 +21,7 @@ PostgREST exposes three database objects of a schema as resources: tables, views
api/aggregate_functions.rst api/aggregate_functions.rst
api/openapi.rst api/openapi.rst
api/preferences.rst api/preferences.rst
api/vary_header.rst
api/* api/*
.. raw:: html .. raw:: html
+16
View File
@@ -0,0 +1,16 @@
.. _vary_header:
Vary Header
===========
In order to assist caching proxies and CDNs, PostgREST includes a ``Vary`` header of value
``Accept, Prefer, Range`` in its responses which should fit most of the bills. As any other
response header, it's available for override
by updating ``response.headers`` GUC variable accordingly, for example:
.. code-block:: postgres
-- Override the Vary header to include Accept, Prefer and X-Test-Vary headers
perform set_config('response.headers', '[{"Vary": "Accept, Prefer, X-Test-Vary"}]', true);
In this case PostgREST will use provided value verbatim.
+21 -10
View File
@@ -60,15 +60,16 @@ import PostgREST.SchemaCache (SchemaCache (..))
import PostgREST.TimeIt (timeItT) import PostgREST.TimeIt (timeItT)
import PostgREST.Version (docsVersion, prettyVersion) import PostgREST.Version (docsVersion, prettyVersion)
import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Char8 as BS
import qualified Data.List as L import qualified Data.List as L
import Data.Streaming.Network (bindPortTCP, import Data.Streaming.Network (bindPortTCP,
bindRandomPortTCP) bindRandomPortTCP)
import qualified Data.Text as T import qualified Data.Text as T
import qualified Network.HTTP.Types as HTTP import qualified Network.HTTP.Types as HTTP
import qualified Network.Socket as NS import qualified Network.HTTP.Types.Header as HTTP (hVary)
import PostgREST.Unix (createAndBindDomainSocket) import qualified Network.Socket as NS
import Protolude hiding (Handler) import PostgREST.Unix (createAndBindDomainSocket)
import Protolude hiding (Handler)
type Handler = ExceptT Error type Handler = ExceptT Error
@@ -208,7 +209,17 @@ postgrestResponse appState conf@AppConfig{..} maybeSchemaCache authResult@AuthRe
where where
toWaiResponse :: ServerTiming -> Response.PgrstResponse -> Wai.Response toWaiResponse :: ServerTiming -> Response.PgrstResponse -> Wai.Response
toWaiResponse timing (Response.PgrstResponse st hdrs bod) = Wai.responseLBS st (hdrs ++ ([serverTimingHeader timing | configServerTimingEnabled])) bod toWaiResponse timing (Response.PgrstResponse st hdrs bod) =
Wai.responseLBS st (hdrs ++ serverTimingHeaders timing ++ [varyHeader | not $ varyHeaderPresent hdrs]) bod
serverTimingHeaders :: ServerTiming -> [HTTP.Header]
serverTimingHeaders timing = [serverTimingHeader timing | configServerTimingEnabled]
varyHeader :: HTTP.Header
varyHeader = (HTTP.hVary, "Accept, Prefer, Range")
varyHeaderPresent :: [HTTP.Header] -> Bool
varyHeaderPresent = any (\(h, _v) -> h == HTTP.hVary)
withTiming :: Handler IO a -> Handler IO (Maybe Double, a) withTiming :: Handler IO a -> Handler IO (Maybe Double, a)
withTiming f = if configServerTimingEnabled withTiming f = if configServerTimingEnabled
@@ -196,6 +196,23 @@
pdSchema: public pdSchema: public
pdVolatility: Volatile pdVolatility: Volatile
- - qiName: custom_vary_hdr
qiSchema: public
- - pdDescription: null
pdFuncSettings: []
pdHasVariadic: false
pdName: custom_vary_hdr
pdParams: []
pdReturnType:
contents:
contents:
qiName: void
qiSchema: pg_catalog
tag: Scalar
tag: Single
pdSchema: public
pdVolatility: Volatile
- - qiName: get_postgres_version - - qiName: get_postgres_version
qiSchema: public qiSchema: public
- - pdDescription: null - - pdDescription: null
+7
View File
@@ -258,3 +258,10 @@ $_$ language sql;
create or replace function notify_pgrst() returns void as $$ create or replace function notify_pgrst() returns void as $$
notify pgrst; notify pgrst;
$$ language sql; $$ language sql;
create or replace function custom_vary_hdr() returns void as $$
begin
perform set_config('response.headers', '[{"Vary": "X-Test-Accept"}]', false);
end
$$ language plpgsql;
+20
View File
@@ -1811,3 +1811,23 @@ def test_client_error_verbosity_config(defaultenv):
"details": None, "details": None,
"hint": "Perhaps you meant the table 'public.items'", "hint": "Perhaps you meant the table 'public.items'",
} }
def test_vary_custom_header_set(defaultenv):
"Test default Vary header value is overridden in pre-request database function"
env = {**defaultenv, "PGRST_DB_PRE_REQUEST": "custom_vary_hdr"}
with run(env=env) as postgrest:
response = postgrest.session.get("/projects")
assert response.headers["Vary"] == "X-Test-Accept"
def test_vary_default_header_set(defaultenv):
"Test default Vary header value matches default one"
with run(env=defaultenv) as postgrest:
response = postgrest.session.get("/projects")
assert response.headers["Vary"] == "Accept, Prefer, Range"