How to set correct headers to serve a static gatsby app

1. Caddy version (caddy version):

2.3.0-alpine on docker

2. How I run Caddy:

in docker on a digital-ocean docker droplet

Dockerfile:

# https://hub.docker.com/_/caddy
FROM caddy/caddy:2.3.0-alpine
COPY Caddyfile /etc/caddy/Caddyfile
COPY /html /html

c. Service/unit/compose file:

version: '3.7'
services:
  db:
    # specify container name to make it easier to run commands
    container_name: ae_db
    restart: always
    image: db
    build:
      context: ./db
    env_file:
      - ./.env
    networks:
      - network
    expose:
      - '5432'
    ports:
      # make the Postgres database accessible from outside the Docker container on port 5432
      - '5432:5432'
    volumes:
      - db_data:/var/lib/postgresql/data
      - sik_data:/sik_data
  graphql:
    container_name: ae_graphql
    restart: unless-stopped
    build:
      context: ./graphql
    networks:
      - network
    expose:
      - '5000'
    ports:
      - '5000:5000'
    depends_on:
      - db
    env_file:
      - ./.env
    command:
      [
        '--connection',
        '${DATABASE_URL}',
        '--schema',
        'ae',
        '--append-plugins',
        'postgraphile-plugin-connection-filter,@graphile-contrib/pg-order-by-related',
        '--jwt-token-identifier',
        'auth.jwt_token',
        '--default-role',
        'anon',
        '--jwt-secret',
        '${JWT_SECRET}',
        '--body-size-limit',
        '300kb',
        '--cors',
        '--disable-query-log',
        '--enable-query-batching',
        '--retry-on-init-fail',
      ]
  json:
    container_name: ae_json
    restart: always
    build:
      context: ./json
    networks:
      - network
    expose:
      - '4000'
    ports:
      - '4000:4000'
    depends_on:
      - db
    env_file:
      - ./.env
  caddy:
    build:
      context: ./caddy
    container_name: ae_caddy
    networks:
      - network
    depends_on:
      - graphql
      - json
    restart: always
    # original image downgrades user but that seems not to work
    # see: https://caddy.community/t/basic-docker-compose-setup-failing/6892/7?u=alexander_gabriel
    user: root
    ports:
      - '80:80'
      - '443:443'
    env_file:
      - ./.env
    volumes:
      - ./caddy/Caddyfile:/etc/caddy/Caddyfile
      - caddy_certs:/root/.local/share/caddy
      - caddy_config:/root/.config/caddy
volumes:
  db_data:
  sik_data:
  caddy_certs:
  caddy_config:
networks:
  network:

3. The problem I’m having:

I am using this Caddyfile:

artdaten.ch, www.artdaten.ch {
  # how to set headers for gatsby?
  # https://caddyserver.com/docs/caddyfile/directives/header
  root * /html
  encode zstd gzip
  file_server
}

api.artdaten.ch {
  reverse_proxy /graphql* {
    to ae_graphql:5000
    header_down Access-Control-Allow-Origin *
  }
  reverse_proxy /graphiql* {
    to ae_graphql:5000
    header_down Access-Control-Allow-Origin *
  }
  reverse_proxy /json-api-test {
    to ae_json:4000
    header_down Access-Control-Allow-Origin *
  }
  reverse_proxy /artendb/* {
    to ae_json:4000
    header_down Access-Control-Allow-Origin *
  }
  reverse_proxy /evab* {
    to ae_json:4000
    header_down Access-Control-Allow-Origin *
  }
  reverse_proxy /alt* {
    to ae_json:4000
    header_down Access-Control-Allow-Origin *
  }
}

The build copies the static files created by gatsby from the public folder into the correct html folder:
gatsby build && rimraf backend/caddy/html && copyfiles -u 1 \"public/**/*\" backend/caddy/html

But then running npx check-gatsby-caching https://artdaten.ch to check the headers gives:

                                                                             
HTML                                                                         
https://artdaten.ch                                                          
❌                                                                            
                                                                             
- Expected                                                                   
+ Received                                                                   
                                                                             
  Object {                                                                   
-   "max-age": 0,                                                            
-   "must-revalidate": true,                                                 
-   "public": true,                                                          
+   "null": "No cache-control header set",                                   
  }                                                                          
                                                                             
                                                                             
app-data.json                                                                
https://artdaten.ch/page-data/app-data.json                                  
❌                                                                            
                                                                             
- Expected                                                                   
+ Received                                                                   
                                                                             
  Object {                                                                   
-   "max-age": 0,                                                            
-   "must-revalidate": true,                                                 
-   "public": true,                                                          
+   "null": "No cache-control header set",                                   
  }                                                                          
                                                                             
                                                                             
Page Data                                                                    
https://artdaten.ch/page-data\index\page-data.json                           
❌                                                                            
                                                                             
- Expected                                                                   
+ Received                                                                   
                                                                             
  Object {                                                                   
-   "max-age": 0,                                                            
-   "must-revalidate": true,                                                 
-   "public": true,                                                          
+   "null": "No cache-control header set",                                   
  }                                                                          
                                                                             
                                                                             
JavaScript and CSS                                                           
https://artdaten.ch/webpack-runtime-5634eb39a001e537d772.js                  
❌                                                                            
                                                                             
- Expected                                                                   
+ Received                                                                   
                                                                             
  Object {                                                                   
-   "immutable": true,                                                       
-   "max-age": 31536000,                                                     
-   "public": true,                                                          
+   "null": "No cache-control header set",                                   
  }                                                                          

So my question is:
How can I for example set headers for https://artdaten.ch/page-data\index\page-data.json to be:

"max-age": 0
"must-revalidate": true
"public": true

?

I checked header (Caddyfile directive) — Caddy Documentation and tried a few variants but I do not seem to understand it.

You should use caddy:2.3.0-alpine instead, which is the actual official image. caddy/caddy is the “old” repo. Our CI still happens to push there, but no guarantees.

Those are not the right paths to persist. See the docs in Docker Hub, you should persist /data and /config.

You can replace all these with one header Access-Control-Allow-Origin * directive (outside of the reverse_proxy blocks)

You can group all these up like this:

@ae_json path /json-api-test /artendb/* /evab* /alt*
reverse_proxy @ae_json ae_json:4000

This uses a named matcher with the path matcher. Gets rid of a lot of duplication. You can do the same for your ae_graphql ones as well.

Probably something like this:

header Cache-Control "public, max-age=31536000, immutable"

See here:

2 Likes

Thanks, lot’s of great advice there!

How do I diferentiate headers for different files, as some need max-age 0 and others higher values?

Use matchers!

1 Like

This topic was automatically closed after 30 days. New replies are no longer allowed.