Skip to content

Instantly share code, notes, and snippets.

@ryanburnette
Last active April 1, 2024 07:54
Show Gist options
  • Star 34 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save ryanburnette/d13575c9ced201e73f8169d3a793c1a3 to your computer and use it in GitHub Desktop.
Save ryanburnette/d13575c9ced201e73f8169d3a793c1a3 to your computer and use it in GitHub Desktop.
Caddy v2.1+ CORS whitelist
(cors) {
@cors_preflight{args.0} method OPTIONS
@cors{args.0} header Origin {args.0}
handle @cors_preflight{args.0} {
header {
Access-Control-Allow-Origin "{args.0}"
Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS"
Access-Control-Allow-Headers *
Access-Control-Max-Age "3600"
defer
}
respond "" 204
}
handle @cors{args.0} {
header {
Access-Control-Allow-Origin "{args.0}"
Access-Control-Expose-Headers *
defer
}
}
}
myawesomewebsite.com {
root * /srv/public/
file_server
import cors https://member.myawesomewebsite.com
import cors https://customer.myawesomewebsite.com
}
@ryanburnette
Copy link
Author

@rhaksw
Copy link

rhaksw commented Oct 5, 2021

According to a stack overflow comment,

You should always append Vary: Origin header when you want to use multiple URLs, see: fetch.spec.whatwg.org/#cors-protocol-and-http-caches

(cors) {
  @origin{args.0} header Origin {args.0}
  header @origin{args.0} Access-Control-Allow-Origin "{args.0}"
  header @origin{args.0} Vary Origin
}

@mildsunrise
Copy link

for anyone who lands here and is confused too: this method isn't a "full" solution as it requires the backend (file_server in this case, or the upstream for the case of reverse_proxy) to already support the OPTIONS method (for the preflight requests).

some routers such as koa-router support it out of the box and in this case, these headers are the only thing you need. for other things, these headers alone don't fully enable CORS.

@ryanburnette
Copy link
Author

Good comments, folks. I’m going to update this soon.

@benjamin658
Copy link

@mildsunrise To enable OPTIONS for the preflight request:

@options {
    method OPTIONS
}

respond @options 204

@prawee
Copy link

prawee commented Mar 29, 2022

thank you. save my day.

@ryanburnette
Copy link
Author

@prawee Glad to hear it. I just updated the gist to include good advice from the comments.

@DER-SSt
Copy link

DER-SSt commented Oct 27, 2022

I added the line:

header @origin{args.0} Access-Control-Allow-Headers "content-type, x-requested-with"

without it, I got the error: "[...] blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response"

@ryanburnette
Copy link
Author

@DER-SSt Thanks!

@DER-SSt
Copy link

DER-SSt commented Oct 27, 2022

yes, thanks for the code!

@C8opmBM
Copy link

C8opmBM commented Mar 2, 2023

(cors) {
        @cors_preflight{args.0} method OPTIONS
        @cors{args.0} header Origin {args.0}

        handle @cors_preflight{args.0} {
                header {
                        Access-Control-Allow-Origin "{args.0}"
                        Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS"
                        Access-Control-Allow-Headers *
                        Access-Control-Max-Age "3600"
                        defer   #turn on defer on your header directive to make sure the new header values are set after proxying
                }
                respond "" 204
        }

        handle @cors{args.0} {
                header {
                        Access-Control-Allow-Origin "{args.0}"
                        Access-Control-Expose-Headers *
                        defer
                }
        }
}
myawesomewebsite.com {
	root * /srv/public/
	file_server

	import cors https://member.myawesomewebsite.com
	import cors https://customer.myawesomewebsite.com
}

@zuohuadong
Copy link

@C8opmBM

respond "" 204

is it right ?

@C8opmBM
Copy link

C8opmBM commented Mar 10, 2023

@mmm8955405
Copy link

(cors) {
        @cors_preflight{args.0} method OPTIONS
        @cors{args.0} header Origin {args.0}

        handle @cors_preflight{args.0} {
                header {
                        Access-Control-Allow-Origin "{args.0}"
                        Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS"
                        Access-Control-Allow-Headers *
                        Access-Control-Max-Age "3600"
                        defer   #turn on defer on your header directive to make sure the new header values are set after proxying
                }
                respond "" 204
        }

        handle @cors{args.0} {
                header {
                        Access-Control-Allow-Origin "{args.0}"
                        Access-Control-Expose-Headers *
                        defer
                }
        }
}
myawesomewebsite.com {
	root * /srv/public/
	file_server

	import cors https://member.myawesomewebsite.com
	import cors https://customer.myawesomewebsite.com
}

import cors https://member.myawesomewebsite.com
import cors https://customer.myawesomewebsite.com

Two errors reported

@ryanburnette
Copy link
Author

Thank you @C8opmBM and @mmm8955405. Gist update to reflect your suggestions.

@coolaj86
Copy link

coolaj86 commented Oct 19, 2023

@ryanburnette This is finally making it onto the Webi cheatsheet: https://webinstall.dev/caddy

(though right now it's just in preview at https://next.webinstall.dev/caddy)

@vanodevium
Copy link

When you want to enable CORS for ANY domain, you have to use next configuration:

This is really a very rare case, but in my practice I often configure the caddy in such a way that it stands behind the traefik and is responsible for different domains.

(cors) {
	@cors_preflight method OPTIONS

	header {
		Access-Control-Allow-Origin "{header.origin}"
		Vary Origin
		Access-Control-Expose-Headers "Authorization"
		Access-Control-Allow-Credentials "true"
	}

	handle @cors_preflight {
		header {
			Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE"
			Access-Control-Max-Age "3600"
		}
		respond "" 204
	}
}

http:// {
	root * /srv/public/
	file_server

	import cors {header.origin}
}

Feel free to change exposed headers, methods etc :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment