# Security headers and CORS, in plain words

Checked on 1 November 2026

Security headers are short settings your server sends with every page to tell the browser to block common tricks against your visitors, and a small app needs five: Content-Security-Policy, frame-ancestors or X-Frame-Options, Strict-Transport-Security, X-Content-Type-Options and Referrer-Policy.

Check your site for free with Mozilla’s HTTP Observatory or your browser’s developer tools, then add the missing headers in your host’s settings or your app’s server code. Make sure CORS, the setting that says which other websites may read your app’s answers, never lets every website read your app with your users’ cookies. Most of these fixes are quick, but a database table that strangers can read is more urgent.

LidTight’s free check looks for four of these headers and tests CORS. On Tidybook, our demo app built with Lovable, it rated two missing headers as medium on 1 November 2026, and an open table in the same app as critical.

## What a security header is

When a browser opens your app, the server sends the page plus a few lines of extra information, such as “X-Frame-Options: DENY”. These lines are [HTTP headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers). Visitors never see them, but the browser reads every one.

A security header asks the browser to be stricter: run no scripts from unknown places, do not show this page inside another site, always use an encrypted connection. Without it, the browser uses a looser default, and nothing visibly breaks. Security headers protect your visitors in their browser. They do not protect your database.

## The five headers that matter for a small app

MDN, Mozilla’s reference for web developers, [rates each of these fixes](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides) for impact and difficulty.

| Header | What it blocks | MDN’s rating |
| --- | --- | --- |
| Content-Security-Policy (CSP) | Scripts from places you did not list. If an attacker slips code into your page (cross-site scripting, or XSS), the browser refuses to run it. | High impact, high difficulty |
| frame-ancestors (part of CSP) or X-Frame-Options | Other sites showing your page inside a hidden frame to trick a signed-in user into clicking your buttons (clickjacking). | High impact, low difficulty |
| Strict-Transport-Security (HSTS) | Unencrypted visits. The browser remembers to use HTTPS for your domain and does not let users click past a certificate error. | High impact, low difficulty |
| X-Content-Type-Options: nosniff | The browser guessing a file’s type, so a file you serve as plain text cannot run as a page or a script. | Low impact, low difficulty |
| Referrer-Policy | Full page addresses leaking to other sites, with any private paths or codes in them. | Low impact, low difficulty |

### Content-Security-Policy

[CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP) is the strongest of the five and the hardest to get right: a strict policy can block your own fonts, analytics or database calls until you list them. MDN advises [testing a policy first](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/CSP) with the Content-Security-Policy-Report-Only header, which reports problems instead of blocking. A CSP is a second layer of defense. It does not replace cleaning what users type. Each part of a CSP works on its own, so you can start with framing protection alone.

### Framing protection

In [clickjacking](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/Clickjacking), another site loads your page in an invisible frame and puts its own button in the same place as one of yours, so the user’s click lands in your app, signed in as them. Without a header, [the browser lets any site embed your page](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Frame-Options). MDN advises setting both frame-ancestors and X-Frame-Options, as real headers: [frame-ancestors](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/frame-ancestors) is not supported in a meta tag, and X-Frame-Options in a meta tag has no effect.

### HSTS, nosniff and Referrer-Policy

[HSTS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Strict-Transport-Security) makes the browser switch every visit to your domain to HTTPS, for as many seconds as its max-age says. Send it only when your whole site works on HTTPS.

[X-Content-Type-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Content-Type-Options) has one value, nosniff, and [MDN says all sites should send it](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/MIME_types). It matters most when users upload files: it stops an uploaded text file from running as a web page. See [Can anyone upload files to your app at your cost?](https://lidtight.com/guides/file-upload-abuse)

[Referrer-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Referrer-Policy) decides how much of your page address other sites learn. Today’s default, strict-origin-when-cross-origin, tells them only your domain. The default was looser before, so set it yourself.

## What CORS is, and the one mistake to avoid

Under the [same-origin policy](https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Same-origin_policy), a page from one origin (the protocol, domain and port, such as https://yourapp.com) cannot read answers from another. This stops a bad website from reading a user’s webmail while they are signed in.

[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS) (Cross-Origin Resource Sharing) is how your server makes exceptions. The Access-Control-Allow-Origin header names the sites whose pages may read the answer. Access-Control-Allow-Credentials: true lets those pages read answers to requests that carry the user’s cookies.

### The mistake: every site, with cookies

The value * (any site) does not work with cookies: [the browser blocks the answer](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS). One way around this is to copy back whatever origin the request names, and allow cookies. Then a page on any website can read what your app shows a signed-in user. [MDN warns against exactly this](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/CORS). Its fix:

- If other sites must read your app with cookies, check the request’s origin against a fixed list and name only that site in the answer, with Vary: Origin, which tells caches that the answer depends on the asking site.
- If your data is public and needs no cookies, * is fine, without Access-Control-Allow-Credentials.
- Otherwise, send neither header.

```
Access-Control-Allow-Origin: https://www.yourapp.com
Access-Control-Allow-Credentials: true
Vary: Origin
```

> **CORS is a browser rule, not a lock on your data.** [MDN describes CORS](https://developer.mozilla.org/en-US/docs/Glossary/CORS) as the system that decides whether browsers block a page’s code from reading another site’s answers. A stranger does not need a browser. Our free check is a program on a server, and it found Tidybook’s reviews table open to anyone (below). Your database rules protect your data. See [My Supabase public key is visible. Is that a problem?](https://lidtight.com/guides/supabase-public-key)

## How serious a missing header is

On 1 November 2026, LidTight’s free check ran on Tidybook, our demo app built with Lovable. It reported this finding as medium: “Some browser protections are switched off · Missing: content security policy, protection against framing”. Its note says these settings “are quick to turn on.”

The same run rated Tidybook’s reviews table critical: 12 rows with first names, emails and phone numbers, readable without signing in. An open table gives your customers’ data to anyone, today. A missing header leaves visitors open to tricks such as clickjacking, or removes a second layer against injected scripts. Fix your tables first (see [What can a stranger see in your app?](https://lidtight.com/guides/what-strangers-see)), then add the headers.

What the free check covers: it looks for HSTS, CSP, nosniff and framing protection on the page at the link you enter. It checks that each header is there, not how strict it is, and it skips Referrer-Policy. For CORS, it asks your page, and the read-only endpoints it finds in your code, as if from another website. It reports an app that copies that site back and allows cookies: high when the app signs in with cookies, low when it does not. The Tidybook run reported no CORS finding.

## How to check your own site for free

1. Open [MDN HTTP Observatory](https://developer.mozilla.org/en-US/observatory), Mozilla’s free header checker, enter your app’s domain and click Scan. It gives a grade and a fix for each problem, with a link to MDN’s guide for it.
2. Open your app in your browser and open the [developer tools](https://developer.mozilla.org/en-US/docs/Learn_web_development/Howto/Tools_and_setup/What_are_browser_developer_tools): Ctrl + Shift + I or F12 on Windows, ⌘ + ⌥ + I on a Mac.
3. Click the Network tab and reload the page. The tools [record requests only while they are open](https://developer.chrome.com/docs/devtools/network/reference).
4. Click the request for your page’s address, open the Headers tab and read Response Headers. Look for the five names above.
5. If your app shows a CORS error, open the Console tab. [MDN says](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS) only the console shows what went wrong.
6. Run [our free check](https://lidtight.com/#check-now) to see the headers next to what matters more: open tables, secret keys in your code and files that should not be public.

> Anyone can scan any domain with Observatory, and each domain’s scan history is public, [its FAQ says](https://developer.mozilla.org/en-US/observatory/docs/faq). The same FAQ says an A+ grade does not mean a site is secure: Observatory does not test for things like outdated software or SQL injection.

## How to set headers on your host

These are the hosts whose official docs we checked on 1 November 2026.

| Host | Where to set headers | Watch out |
| --- | --- | --- |
| [Netlify](https://docs.netlify.com/manage/routing/headers/) | A file named _headers in your publish folder, or a headers section in netlify.toml | Not applied to pages made by functions, such as server-rendered pages. Those must send their own headers. |
| [Cloudflare Pages](https://developers.cloudflare.com/pages/configuration/headers/) | A file named _headers in your static files folder, often public/ | Not applied to responses from Pages Functions or server-rendered frameworks. Set the headers in that code. |
| [Vercel](https://vercel.com/docs/project-configuration/vercel-json) | A headers list in vercel.json | Applies to static files and functions, matched by path. |

A starting _headers file for Netlify or Cloudflare Pages:

```
/*
  Content-Security-Policy: frame-ancestors 'none'
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
  Strict-Transport-Security: max-age=63072000
```

Its CSP line only controls who may frame your page, so it cannot block your scripts. Grow the policy later, with Report-Only first. To show your app inside another site of yours, list that site in frame-ancestors. The max-age value, two years, is [the one MDN recommends](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/TLS).

**Lovable hosting.** We found no custom header setting in Lovable’s [hosting](https://docs.lovable.dev/features/hosting), publishing and security docs. Lovable apps created from 13 May 2026 use server-side rendering, so if you [move one to Netlify or Cloudflare Pages](https://docs.lovable.dev/tips-tricks/external-deployment-hosting), set its headers in code, not only in _headers.

If you cannot set headers at all, [a CSP can go in a meta tag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP). Framing protection cannot, so ask your host for it. For the rest of your launch checks, see [Before you go live: a security checklist](https://lidtight.com/guides/launch-security-checklist).

## Sources

1. [MDN: HTTP headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers)
2. [MDN: Practical security implementation guides](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides)
3. [MDN: Content Security Policy (CSP) guide](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP)
4. [MDN: Content Security Policy (CSP) implementation](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/CSP)
5. [MDN: Clickjacking](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/Clickjacking)
6. [MDN: X-Frame-Options header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Frame-Options)
7. [MDN: CSP frame-ancestors directive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/frame-ancestors)
8. [MDN: Strict-Transport-Security header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Strict-Transport-Security)
9. [MDN: X-Content-Type-Options header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Content-Type-Options)
10. [MDN: MIME type verification](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/MIME_types)
11. [MDN: Referrer-Policy header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Referrer-Policy)
12. [MDN: Same-origin policy](https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Same-origin_policy)
13. [MDN: Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS)
14. [MDN: Cross-Origin Resource Sharing (CORS) configuration](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/CORS)
15. [MDN glossary: CORS](https://developer.mozilla.org/en-US/docs/Glossary/CORS)
16. [MDN HTTP Observatory](https://developer.mozilla.org/en-US/observatory)
17. [MDN HTTP Observatory: FAQ](https://developer.mozilla.org/en-US/observatory/docs/faq)
18. [MDN: Transport Layer Security (TLS) configuration](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/TLS)
19. [MDN: What are browser developer tools?](https://developer.mozilla.org/en-US/docs/Learn_web_development/Howto/Tools_and_setup/What_are_browser_developer_tools)
20. [Chrome DevTools: Network features reference](https://developer.chrome.com/docs/devtools/network/reference)
21. [Netlify: Custom headers](https://docs.netlify.com/manage/routing/headers/)
22. [Cloudflare Pages: Headers](https://developers.cloudflare.com/pages/configuration/headers/)
23. [Vercel: Static configuration with vercel.json](https://vercel.com/docs/project-configuration/vercel-json)
24. [Lovable: How Lovable hosts your app](https://docs.lovable.dev/features/hosting)
25. [Lovable: Deploying and hosting outside Lovable](https://docs.lovable.dev/tips-tricks/external-deployment-hosting)
