Resolving CORS error between front-end & back-end Golang binaries

1. Output of caddy version:

v2.5.2 h1:eCJdLyEyAGzuQTa5Mh3gETnYWDClo1LjtQm2q9RNZrs=

2. How I run Caddy:

Caddy is being run on Ubuntu 20.04.4 LTS on a Linode.
I use a ‘general’ Caddyfile and then import separate domain Caddyfiles which have the content of the general file imported into each of them as required (see below)

a. System environment:

I am using 2 compiled Golang binaries, one front-end and one back-end.

b. Command:

I simply start and stop Caddy as follows after each config change…

sudo service caddy stop
sudo service caddy start

c. Service/unit/compose file:

N/A

d. My complete Caddy config:

General config:

{
  email  my.email@gmail.com
}

(static) {
  @static {
    file
    path *.ico *.css *.js *.gif *.jpg *.jpeg *.png *.svg *.woff *.json
  }
  header @static Cache-Control max-age=2592000
}

(security) {
  header {
    # enable HSTS
    Strict-Transport-Security max-age=31536000;
    # disable clients from sniffing the media type
    X-Content-Type-Options nosniff
    # keep referrer data off of HTTP connections
    Referrer-Policy no-referrer-when-downgrade
  }
}

import conf.d/*.conf

Specific example domain config for this case (as imported)

www.example.org, example.org {
        encode zstd gzip
        import static
        import security


        # used only when serving static index.html
        # root * /var/www/example.org
        # file_server



        log {
                output file /var/log/caddy/example.org-access.log
        }



        reverse_proxy http://localhost:8090
#           @options {
#               method OPTIONS
#           }
#           header {
#               Access-Control-Allow-Origin *
#               Access-Control-Allow-Credentials true
#               Access-Control-Allow-Methods *
#               Access-Control-Allow-Headers *
#               defer
#           }
            reverse_proxy * {
                to http://localhost:8091
                header_down Access-Control-Allow-Origin *
#               defer
            }
#           respond @options 204
}

3. The problem I’m having:

Ok, I will need to explain the above ! My ‘general’ Caddyfile causes no issues, it’s commonly in use with other domains served from this Linode.
The front-end reverse_proxy (:8090) serves correctly. The config code following that (much of which is commented out as a result of trying a multitude of different ideas) relating to the back-end is the problem. I know it probably looks pretty silly but what’s uncommented is where it stands at the moment and leaving the commented-out stuff gives you some idea of where I’ve been going (or not) with this. I just don’t know what to do next.

4. Error messages and/or full log output:

    $ journalctl -u caddy --no-pager | less +G yields no entries.

Browser console outputs:
caddy-cors-error

…on attempting to pass authentication and fetch reply from back-end.

Paste logs/commands/output here.
Currently nothing to add here.

5. What I already tried:

None of my attempts at writing a config to allow free flow between front-end (:8090) and back-end (:8091) succeed. All result in CORS failure. I suspect this will turn out to be just a simple configuration statement to reverse_proxy the back-end while accommodating CORS but I haven’t hit on it yet after several hours of attempts. What should I be doing to achieve this please ?

6. Links to relevant resources:

None at present, I have found no links which cover Caddy v2 and CORS together in detail.

Hi :wave:

I would argue CORS headers should be added by the backend itself, but if that isn’t viable, you could also very much do that on reverse_proxy level (caddy).

Consider reading Cross-Origin Resource Sharing (CORS) - HTTP | MDN, if you haven’t already.

You could use both header_down within the reverse_proxy directive

api.example.com {
  reverse_proxy localhost:8091 {
    header_down Access-Control-Allow-Origin *
  }
}

or the header (Caddyfile directive) — Caddy Documentation outside reverse_proxy.

api.example.com {
  header Access-Control-Allow-Origin *
  reverse_proxy localhost:8091
}

That being said, why does your browser try to connect to localhost:8091 directly?
Is your frontend aware that the backend is reverse_proxied by caddy and not at localhost:8091?

1 Like

Hi, thanks for taking a look at this. I have implemented both of the suggestions you made but I am still unfortunately up against the same CORS error. I have replaced the trial & error mess of my first Caddyfile post with the following configuration but as mentioned I still have the CORS error…

www.bikeactivenorfolk.org, bikeactivenorfolk.org {
        encode zstd gzip
        import static
        import security

        # used only when serving static index.html
        # root * /var/www/bikeactivenorfolk.org
        # file_server

        log {
                output file /var/log/caddy/bikeactivenorfolk.org-access.log
        }

        reverse_proxy http://localhost:8090
}

api.bikeactivenorfolk.org {
        encode zstd gzip
        import static
        import security

        log {
                output file /var/log/caddy/bikeactivenorfolk.org-access.log
        }

        header Access-Control-Allow-Origin *

        reverse_proxy http://localhost:8091
}

I was under the impression that the api back-end had to be exposed and reachable by browsers, which this is. This is being live tested at this time and I’ve left it all in place. Virtually all the api routes are protected but by way of example here’s one that isn’t. It will query the database and return a JSON response which proves :8091 is reachable…

API url

If you were to check out the front-end :8090 though e.g. Login and put in anything you like by way of credentials,then the CORS error will be raised. I really don’t know what I’m doing wrongly here, all seems accessible but it’s ‘just’ CORS. Both front & back-ends integrate perfectly on local development machine.

Is your site currently down?
Because otherwise I could just do some curl requests or maybe even be able to check whether http://localhost:8091 is hard-coded somewhere in the frontend or whatever.

Your Caddyfile looks alright and should do want you are trying to archive :innocent:

Could you go into detail who your frontend/backend stack looks like?
Because I really do have a feeling that you all you have to do now is just changing some config param (or in the worst case recompile) :thinking:

1 Like

Hi, I just checked, right now the site is down :roll_eyes: Just received this from Linode…

Notifications

We are aware of an issue affecting service in London, UK. If you are experiencing service issues in this facility, there is no need to open a support ticket at this time. Please monitor our status blog at https://status.linode.com for further information. Thank you for your patience and understanding.

Thanks for your recent post, I appreciate it. I cannot do anymore with resurrecting the site at the moment but I will do so as soon as the Linode returns. It’s just bad timing :confused: and if it’s anything like where I am, they’re overheating :joy:

The 2 Golang binaries can be recompiled very quickly but having said that they were recompiled only a few hours ago to ensure they were not obviously the source of the problem. That is not to say that they are definitely not but the same binaries have no issues at local development which of course does not incorporate Caddy. That was the main reason for initially considering it as the source of the problem, or at least my bad configuration of it.

I’ll post again as soon as I resurrect the site and if you’d be ok to do the curl requests as is convenient, that would be great, thanks again.

2 Likes

Hi, the Linode is up, the site is up and so am I (I think :wink:)

I’ve recompiled the binaries. Any front-end operation requiring requiring a back-end response still has the ever present CORS error. Most of the back-end links are obviously protected but there is one you can hit to prove it’s working (it’ll just provide a meaningless JSON response from a database query)…

Back-end: API

Front-end: BikeActive Norfolk

Thanks again, it will be great to pin this down, currently it has me stumped

1 Like

Yeah, so

see the page source of https://bikeactivenorfolk.org/login for example (view-source:https://bikeactivenorfolk.org/login):

There is a localhost:8091 in line 395:

    fetch('http:\/\/localhost:8091/api/authenticate', requestOptions)
      .then(response => response.json())

And because of that, your browser will only ever make a request to localhost:8091.

You will have to change that in your software’s source code and recompile your binary :innocent:

Another example would be https://bikeactivenorfolk.org/change-password.

2 Likes

Oh dear, yeah indeed… it’s embarrassing :roll_eyes: This all stems from a basic lack of understanding and not exposing the API in the first place. Now of course I’m going to need to do a significant amount of API rerouting as my punishment. One thing to remember though is that I did still need the Caddy configuration assistance, which I’m sure will be fine once I settle this issue too. Also, if I look back at the image I posted of the console error localhost:8091 is staring me in the face but with the way I was thinking about it, that’s what I thought I should see… lessons learned.

I’ll post back in a day or two when I’ve sorted it but I very strongly suspect it’s resolved.

Thanks a million for sticking with this, it’s really appreciated.

2 Likes

FYI you can just change it to /api/authenticate and it should work; it’ll use the same domain/scheme as the original page which loaded the site, so this would work in general, no matter how you run the site.

2 Likes

Thanks for this information, I can see how it could help ease the transition from development to a live environment and bear it mind for the future :sunglasses:

As you would suspect this, alongside your help with the format of my Caddyfile has resolved the issue.
I still have a single issue with being unable to access a remote smtp server wihich I’m trying to resolve, I just have to work out exactly where to put an Access-Control-Allow-Origin header to let this happen but I’ll get there.
Several lessons learned here about Caddy and CORS and the help has been genuinely appreciated, many thanks

3 Likes