Learning Caddy - Security Headers & CORS
Episode 20 of 31

Learning Caddy - Security Headers & CORS

This episode covers security at the header level: the header directive for adding, changing, and removing headers, security headers like CSP and HSTS, complete CORS configuration with preflight, and practices for hiding server information.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Many web attacks are prevented not in the application, but at the protocol level — through HTTP headers. Browsers read headers like Content-Security-Policy and X-Frame-Options to make security decisions before rendering a page. Episode 20 covers how Caddy controls these headers.

You'll learn the header directive to add, change, and remove headers, the list of security headers you should install, complete CORS configuration for cross-domain APIs, and how to hide server information from the public.

Headers are a cheap yet highly effective defense layer — and all of it can be done from the Caddyfile without touching your application.

The header Directive

Adding and Removing Headers

header adds headers to every response:

Add and remove headers
example.com {
    header {
        X-Frame-Options DENY
        X-Content-Type-Options nosniff
        Referrer-Policy strict-origin-when-cross-origin
        -Server
    }
    root * /var/www
    file_server
}

The rules: new header names are added; names prefixed with - are removed. -Server removes the Server header Caddy sends, hiding the technology behind the site.

Conditional Headers

Headers can be restricted with a matcher:

Conditional header
example.com {
    @api {
        path /api/*
    }
    header @api Access-Control-Allow-Origin *
    root * /var/www
    file_server
}

header @api Access-Control-Allow-Origin * adds the CORS header only to API paths.

Important Security Headers

Clickjacking and MIME Sniffing Protection

Some headers are essential for modern sites:

Basic security headers
example.com {
    header {
        X-Frame-Options DENY
        X-Content-Type-Options nosniff
        X-XSS-Protection 0
        Permissions-Policy geolocation=(), camera=(), microphone=()
    }
    root * /var/www
    file_server
}
  • X-Frame-Options DENY: the site can't be embedded in an iframe (anti-clickjacking).
  • X-Content-Type-Options nosniff: browsers don't guess file types (anti-MIME sniffing).
  • Permissions-Policy: restricts browser API access like camera and GPS.

X-XSS-Protection 0 actually disables an old, problematic browser feature; modern XSS protection comes from CSP.

Content-Security-Policy (CSP)

CSP is the strongest defense against XSS. This header tells browsers which content sources are allowed:

Basic CSP
example.com {
    header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'"
    root * /var/www
    file_server
}

default-src 'self' only allows content from your own domain. External scripts are blocked unless added to the list.

HSTS

The HSTS header forces browsers to always use HTTPS:

HSTS
example.com {
    header Strict-Transport-Security "max-age=63072000; includeSubDomains"
    root * /var/www
    file_server
}

header Strict-Transport-Security ... with a two-year max-age and includeSubDomains rejects HTTP connections for the entire domain. We already met it in episode 8.

CORS Configuration

The CORS Concept and Preflight

CORS lets browsers call APIs from another domain. The browser sends an OPTIONS preflight request before the actual request, and Caddy must respond correctly:

CORS for an API
api.example.com {
    @preflight {
        method OPTIONS
    }
 
    handle @preflight {
        header Access-Control-Allow-Origin https://app.example.com
        header Access-Control-Allow-Methods "GET, POST, PUT, DELETE"
        header Access-Control-Allow-Headers "Content-Type, Authorization"
        header Access-Control-Max-Age "86400"
        respond 204
    }
 
    header Access-Control-Allow-Origin https://app.example.com
    reverse_proxy localhost:8080
}

The @preflight block handles OPTIONS requests and returns 204 without touching the backend. The Access-Control-Allow-Origin header on normal responses lets browsers read the response.

Allow-Credentials

If the application uses cross-domain cookies:

CORS with credentials
api.example.com {
    header Access-Control-Allow-Origin https://app.example.com
    header Access-Control-Allow-Credentials true
    reverse_proxy localhost:8080
}

Access-Control-Allow-Credentials true allows cookies to be sent with cross-domain requests. Note: with credentials, Allow-Origin can't be an asterisk — it must be a specific domain.

Best Practices

Testing Headers

After applying headers, test the results:

Check response headers
curl -I https://example.com

curl -I shows the response headers received. Make sure security headers appear and the Server header doesn't leak. Tools like securityheaders.com can give an automated assessment.

  • X-Frame-Options: DENY
  • X-Content-Type-Options: nosniff
  • A tailored Content-Security-Policy
  • Referrer-Policy: strict-origin-when-cross-origin
  • Strict-Transport-Security (HTTPS)
  • A Permissions-Policy that restricts browser APIs
  • Remove Server and backend headers

Conclusion

Episode 20 completed security at the header level: the header directive for adding, changing, and removing headers, security headers like CSP, HSTS, and X-Frame-Options, complete CORS configuration with OPTIONS preflight and credentials, and practices for hiding server information.

Key takeaways:

  • header adds headers; a - prefix removes them.
  • -Server hides backend technology from the public.
  • CSP is the primary defense against XSS.
  • OPTIONS preflight must be answered with 204.
  • Allow-Credentials true requires a specific origin.
  • Test all headers with curl -I.

In the next episode, episode 21, we'll cover compression & caching — the encode directive with gzip, brotli, and zstd, precompressed files, Cache-Control headers for static assets, HTTP/2 and HTTP/3, and performance tuning like keep-alive and timeouts.

Learning Caddy - Security Headers & CORS | Learning Caddy