# Security headers

> The headers every response carries to keep browsers safe, framing, HTTPS and content sniffing, and how to change them.

This guide covers the security headers a Tinka App sends. You will learn how to:

- Know what every response carries, and why
- Let a page be shown in a frame, by your App or a partner
- Keep browsers on HTTPS with HSTS, and extend it to subdomains
- Isolate your pages from the windows that open them
- Set your own Content Security Policy

## Overview

A browser runs your pages next to every other site its user visits, so a few response headers decide what those other sites can do with your pages: show them in an invisible frame to trick a click, downgrade a visit to plain HTTP, or make the browser guess a file is a script. A Tinka App sends safe answers to all of these on every response, before your middleware runs, and `HTTPConfig.Security` changes them.

| Header | Default | Stops |
|---|---|---|
| `Content-Security-Policy: frame-ancestors 'none'` | On | Other sites showing your pages in a frame (clickjacking) |
| `Strict-Transport-Security: max-age=31536000` | On, over HTTPS only | A later visit being downgraded to plain HTTP |
| `X-Content-Type-Options: nosniff` | On | The browser treating an upload or a JSON response as HTML or a script |
| `Referrer-Policy: strict-origin-when-cross-origin` | On | Full URLs, with their IDs and tokens, leaking to other sites |
| `Cross-Origin-Opener-Policy` | Off | Windows your pages open, or that open them, reaching into them |

Requests that change data from another site's page are refused too, before any middleware, as [App reference](../../reference/app.md#what-every-request-gets) describes, unless [CORS](cors.md) lists the site's origin.

## Allow framing

No site can show your pages in a frame, yours included. A page meant to be embedded, such as a widget or a preview, lists who may frame it with `FrameAncestors`, as Content Security Policy sources:

```go title="config/http.go" ins={21-23}
package config

import (
	"strconv"

	"github.com/mucolabs/tinka"
	"github.com/mucolabs/tinka/env"
)

func httpConfig(e *env.Env) tinka.HTTPConfig {
	return tinka.HTTPConfig{
		// Addr is where the App listens. act serve listens for it in
		// development.
		Addr: ":" + strconv.Itoa(e.Int("PORT", 3000)),

		// Debug shows unexpected errors' messages and stacks in responses.
		// It's off unless APP_DEBUG=true, which .env sets on your machine:
		// production reads no .env file, so it stays off there even when
		// APP_ENV is forgotten.
		Debug: e.Bool("APP_DEBUG", false),
		Security: tinka.SecurityConfig{
			FrameAncestors: []string{"'self'", "https://partner.example.com"},
		},
	}
}
```

`'self'` lets your own pages frame each other.

## Keep browsers on HTTPS

Once a browser has reached your App over HTTPS, `Strict-Transport-Security` tells it to use HTTPS for the host for a year, even when a link or a typed address says `http://`. Without it, the first request of a later visit can go over plain HTTP, where anyone on the network can redirect it.

The App sends it only when the request came over HTTPS, either on a TLS connection or through a proxy in `TrustedProxies` that set `X-Forwarded-Proto: https`, because browsers ignore it over plain HTTP. It never sends it to `localhost`, so development over HTTP keeps working.

`HSTS` changes how long it lasts, and a negative value turns it off. `HSTSSubdomains` extends it to every subdomain, and `HSTSPreload` marks the host for the browsers' preload list:

```go title="config/http.go" ins={2-3}
		Security: tinka.SecurityConfig{
			HSTSSubdomains: true,
			HSTSPreload:    true,
		},
```

:::warning
`HSTSSubdomains` makes browsers refuse plain HTTP on every subdomain of your host, for as long as `HSTS` lasts, and a preloaded host can't be taken off quickly. A subdomain that still serves HTTP only, such as an old status page, becomes unreachable. Turn both on once every subdomain serves HTTPS.
:::

## Isolate your pages from other windows

`Cross-Origin-Opener-Policy: same-origin` cuts the link between your pages and windows from other sites that open them, or that they open. It's off by default, because the same link is how a sign-in popup, such as an OAuth provider's, hands its result back to your page. Turn it on when your App opens no such popups:

```go title="config/http.go" ins={2}
		Security: tinka.SecurityConfig{
			OpenerPolicy: "same-origin",
		},
```

## Set your own Content Security Policy

A full Content Security Policy lists where your pages may load scripts, styles and images from, so a script an attacker injects doesn't run. Only you know what your pages load, so the App sets none beyond `frame-ancestors`. Set yours in middleware, and include `frame-ancestors` in it, because your header replaces the App's:

```go title="app/middleware/csp.go"
package middleware

import "github.com/mucolabs/tinka"

// ContentSecurityPolicy lets pages load scripts, styles and images from
// the App itself only.
func ContentSecurityPolicy(c *tinka.Ctx, next tinka.Next) error {
	c.Header().Set("Content-Security-Policy", "default-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'")
	return next(c)
}
```

The App's own HTML error pages carry a strict policy of their own, which allows their one style and nothing else.

See also: [Encryption](encryption.md), [Middleware](../basics/middleware.md), [Request](../basics/request.md#client-ip-address), [App reference](../../reference/app.md)
