Struggling with CORS and reverse_proxy

1. The problem I’m having:

I have a static website at my primary domain, say https://example.com.

On that website, I have an HTMX powered form that posts to a simple Go server that processes the form. caddy acts as reverse_proxy for the Go code.

caddy hosts the reverse_proxy on a subdomain, say https://reg.example.com.

I am struggling to overcome CORS issues, e.g. the following is printed in the browser console:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://reg.example.com/v1/foo. (Reason: CORS preflight response did not succeed). Status code: 405.

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://reg.example.com/v1/foo. (Reason: CORS request did not succeed). Status code: (null).

2. Error messages and/or full log output:

3. Caddy version:

v2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=

4. How I installed and ran Caddy:

Debian package

a. System environment:

Debian Bookworm

d. My complete Caddy config:

# Global options
{
	persist_config off
	admin :8219
	servers {
		metrics
	}
}
# Common Headers
(commonheader) {
	header {
		-Server
		Permissions-Policy interest-cohort=()
		Strict-Transport-Security max-age=31536000;
		X-Content-Type-Options nosniff
		X-Frame-Options DENY
		Referrer-Policy same-origin
		Referrer-Policy origin
		Content-Security-Policy "default-src https:;frame-src https://hcaptcha.com https://newassets.hcaptcha.com;frame-ancestors 'none';"
		+Vary Origin
		defer
	}
}
# My Reg
(myregheader) {
	header {
		Access-Control-Allow-Origin "https://reg.example.com"
	}
}
# My Reg Src
(myregsrc) {
	header {
		Access-Control-Allow-Origin "https://example.com"
	}
}
# HTMX Headers
(htmxheader) {
	header {
		+Access-Control-Allow-Headers "HX-Current-URL,HX-Request"
		+Access-Control-Expose-Headers "HX-Redirect"
	}
}
# Common Fileserver
(commonfileserver) {
	file_server {
		hide .git
	}
}
# Logging
(commonlogging) {
	log {
		format filter {
			wrap console
			fields {
				request>remote_ip ip_mask {
					ipv4 24
					ipv6 48
				}
				request>client_ip ip_mask {
					ipv4 24
					ipv6 48
				}
			}
		}
	}
}
# Common Main
(commonmain) {
	tls foobar@example.com {
		ca https://acme-v02.api.letsencrypt.org/directory
	}
	encode {
		zstd
		gzip
	}
}
# Individual sites
example.com, www.example.com {
	root * /usr/local/share/www/example.com/public
	import commonheader
	import commonfileserver
	import commonlogging
	import commonmain
	import htmxheader
	import myregheader
}
reg.example.com {
  import commonheader                                                                                                       
  import htmxheader 
  import myregsrc
  reverse_proxy localhost:8619
}

CORS is really an application-layer concern. Typically, you should be adding those headers in your application, not in your webserver. Your app should know the right thing to do.

This means your app is rejecting OPTIONS method requests with 405 status (method not allowed). Make sure it accepts them. In fact, it should be adding the CORS headers to those OPTIONS requests.

You can simplify these:

				request>client_ip ip_mask 24 48
1 Like

Thanks for the reply @francislavoie

Is it not possible to do something clever using header_up / header_down or something ?

My “application” is really not fancy, is a simple Go mux http server that handles the POSTs from the form. I would rather keep it that way, otherways it kind of makes Caddy pointless as a reverse proxy since I might as well do the SSL etc. directly in Go and cut out the middleman ?

Sure, but if your app is handling HTML forms, it also needs to handle CORS to be complete. Handle the OPTIONS method, not only POST.

I don’t think it makes Caddy pointless, it’s a very common thing to just have Caddy have a 2 line config (domain + reverse_proxy and that’s it). Caddy’s all about having intelligent defaults, and doing a lot for you with very little config.

But you could do TLS in your app if you want, with GitHub - caddyserver/certmagic: Automatic HTTPS for any Go program: fully-managed TLS certificate issuance and renewal (the lib underlying Caddy)

2 Likes

In CORS, a preflight request automatically made with OPTIONS method by browsers checks if the CORS protocol is understood and which specific access methods and request headers are allowed for a cross-origin request.

A microservice backend normally focuses on its business requirements, domain functions and finally serves response data for legitmate requests.

It makes natural sense to handle the OPTIONS request on reverse proxy side where http traffic routing is managed. It’s much easier to decide which ORIGINs is allowed on reverse proxy, just a piece of change on configuration, rather than requiring i.e. Java code change on microservice backend, redeploying it in Kubernetes containers, which is one of important values and convenience with having a reverse proxy in front of our web/application servers

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.