Cassidy Williams

Software Engineer in Chicago

Cassidy's face

Three ways to set headers with Netlify and Astro

I recently had to build an Astro website on Netlify in which we needed custom headers. Turns out there’s several different ways to do that, some of them are weird, and some of them will work better for your use cases than others.

I’m writing this down here because I know for a fact that I will need to refer back to this again someday, because CORS errors are the worst and omnipresent.


There are also other ways, but I am only going to list three, because these are the three that seem to work the most for me. I’m also just doing it for the Access=Control-Allow-Origin header because you can probably figure out how to add others once you see one.

Also, here’s the Astro documentation for the Netlify integration if you need it! I’m also assuming you have the @astrojs/netlify package installed if you’re using Astro for server-side rendering or for on-demand builders.

Setting headers in your Astro config

The first way to set headers in Astro is directly in the astro.config.mjs file.

// astro.config.mjs
import { defineConfig } from "astro/config";
import netlify from "@astrojs/netlify/functions";

export default defineConfig({
	output: "server",
	server: {
		headers: {
			"Access-Control-Allow-Origin": "*"
	adapter: netlify()

This works great if you’re running your Astro site in server-side rendering mode, but it’s kind of weird on Netlify sometimes (and I admit I don’t know all the cases, like when edge functions are involved or something, etc), so if it doesn’t work, don’t fret, there’s other options!

Setting headers in the Netlify config file

You can set headers in your netlify.toml like so (docs here):

# netlify.toml
  for = "/*"
    Access-Control-Allow-Origin = "*"

There’s also a _headers file that Netlify supports that’s very similar:

# _headers
  Access-Control-Allow-Origin: *

And also similar still is setting a Netlify proxy redirect (again in the netlify.toml) where you take a relative route, point it to an absolute route, and add the header.

# netlify.toml
  from = "/search"
  to = "*"
  status = 200
  force = true
  headers = { Access-Control-Allow-Origin = "*" }

This Netlify config method works best with static sites, not anything served with serverless functions or edge functions.

Setting headers with an edge function as middleware

This is probably the most “advanced” way to set headers, but it works really well, and you can add a lot more custom logic to it (plus I think it has the least restrictions as well).

First of all, you have to make an edge function file in netlify/edge-functions/ (I’ll call mine headers.js), and then point to it in your netlify.toml:

# netlify.toml
	path = "/*"
	function = "headers" # or whatever your file name is

Then, in that headers.js file, you add your headers:

// headers.js
export default async (request, context) => {
	const response = await;

	// You need this if you are calling this from the browser
	// to handle CORS preflight stuff
	if (request.method === "OPTIONS") {
		return new Response("ok", {
			headers: {
				"Access-Control-Allow-Origin": "*",

	response.headers.set("Access-Control-Allow-Origin", "*");
	return response;

If you don’t add that OPTIONS conditional, you might see an error like this whenever you call that edge function:

Access to fetch at '...' from origin '...' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

Anyway, in here you can do whatever logic you’d like around adding headers conditionally, adding multiple headers, whatever your heart wants!

The end

These were all things that I ended up working with for some projects at my work, Contenda, and you can probably find some of these sprinkled in some of our open source repos if you’d like to see some examples.

All this being said…

For future Cassidy: welcome back. I see you’re running into the same CORS issues again. When will you ever learn?

For everyone else: I hope this was helpful for you, ‘til next time!

View posts by tag

#advice #events #technical #learning #musings #work #meta #personal