[v2] CORS Preflight Did Not Succeed

1. Caddy version (caddy version):

2.0.0 docker builder with webdav plugin

2. How I run Caddy:

Running using docker/docker-compose, from a Caddy builder image.

a. System environment:

Debian 10, Docker 19.03.12, Docker-Compose 1.26.0

b. Command:

To build docker image: docker build --tag caddy:2.0.0-custom .
To run docker-compose: docker-compose up -d caddy

c. Service/unit/compose file:

Dockerfile to build image:

FROM caddy:2.0.0-builder AS builder

RUN caddy-builder \

FROM caddy:2.0.0

COPY --from=builder /usr/bin/caddy /usr/bin/caddy


version: '3.4'

# Caddy web server, doing reverse proxy
    image: "caddy:2.0.0-custom"
    container_name: "caddy"
     - "80:80"
     - "443:443"
     - /srv/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
     - /srv/caddy/www:/usr/share/caddy:rw
     - /srv/caddy/data:/data:rw
    restart: always

d. My complete Caddyfile or JSON config:

  order webdav last

# WebDAV
dav.rowland.pw {
  root * /usr/share/caddy/webdav

  log {
    output file /data/logs/caddy_dav.log

  basicauth {
    secureguy JDJhJDEwJEVCNmdaNEg2Ti5iejRMYkF3MFZhZ3VtV3E1SzBWZEZ5Q3VWc0tzOEJwZE9TaFlZdEVkZDhX

  encode gzip zstd

  header {
    Access-Control-Allow-Origin *
    Access-Control-Allow-Credentials true
    Access-Control-Allow-Headers "Authorization,DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,X-Accept-Charset,X-Accept,origin,accept,if-match,destination,overwrite,X-CustomHeader"
    Access-Control-Expose-Headers "ETag"
    Access-Control-Max-Age 1728000


# KeeWeb/KeePass
kee.rowland.pw {
  root * /usr/share/caddy/keeweb
  file_server /*

  log {
    output file /data/logs/caddy_kee.log

  encode gzip zstd

3. The problem I’m having:

I was using Caddy v1 for a long time, and as such I had moved my password manager to KeeWeb. This ran for a long time with not issues. Recently the drive died on my NUC, so I replaced it and I’m rebuilding from backup with Caddy v2. I posted before, listed below in Section 6, about BasicAuth issues and those got resolved. Now I’m trying to get 2x different sites to talk to each other. kee.domain.tld is KeeWeb, dav.domain.tld is the WebDAV.

4. Error messages and/or full log output:

In Firefox, when I try to connect to the WebDav from KeeWeb site, I see the error CORS Preflight Did Not Succeed using the Web Dev Tools/Network console.

From my caddy log for the webdav site:

    "msg":"handled request",
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"
                "gzip, deflate, br"
    "common_log":" - - [10/Jul/2020:20:05:12 +0000] \"OPTIONS /keepass/drowland.kdbx HTTP/1.1\" 401 0",
            "Basic realm=\"restricted\""

5. What I already tried:

If I use CyberDuck or Windows 10 Explorer, the WebDAV doesn’t have an issue authenticating and allowing transfers. So I don’t believe it’s a WebDAV plugin issue, I think I’m just missing a header or two.

I was missing a few headers from my v1 config in my v2 config of my previous post, so I’ve added them, but it’s still not connecting.

6. Links to relevant resources:

Original Post - [v2] BasicAuth not working with docker image or custom build

I think the issue here is the 401 Unauthorized for /keepass/drowland.kdbx.

A CORS preflight request requires a 204 No Content response.

You need to ensure your client is authorized (i.e. via basic auth).

Alternately you could exclude OPTIONS requests from your basicauth directive… This might be a security concern depending on your threat model.

I’ve written a bit in the past about faking CORS preflight responses as well in case there’s something there that helps: V2: Imitate a correct OPTIONS request - #2 by Whitestrake

1 Like

I tried removing the OPTIONS from Access-Control-Allow-Methods header, and it didn’t seem to make any difference.

I agree the HTTP 401 is the issue, but the webpage I am trying to connect with is the one that supposed to be sending the Authorization. On KeeWeb, I click on WebDav to specify the file path and credentials, I enter the info, then hit OK to connect. So is KeeWeb not sending the credentials?

Based on your “Imitate a correct OPTIONS request” post and this post about Apache, what would the 204 CORS Pre-flight imitation look like?

@options {
  method OPTIONS
respond @options 204

You mentioned adding headers into this imitation and I’m not sure I understand how or where.

Caddy’s basicauth module expects an Authorization header with matching base64-encoded credentials.

In basic HTTP authentication, a request contains a header field in the form of Authorization: Basic <credentials> , where credentials is the Base64 encoding of ID and password joined by a single colon : .

Basic access authentication - Wikipedia

If KeeWeb is correctly forming this Authorization header for its requests, the basicauth directive will not respond with a 401 for it. So we can infer that KeeWeb is either not sending the basic auth credentials or not forming it correctly.

We can confirm that it is missing by looking at your own supplied log output above:

Noting the complete lack of Authorization request header.

You should already be sending some headers, the usual CORS headers, so just one to add. From my other post, emphasis mine:

As for adding the single header you’d need, and responding, this is the relevant part of the post:

handle isn’t necessary, it’s just more readable in my opinion.

Note also that if you’re doing this across the entire board for all OPTIONS requests, you might want to narrow it down a bit more. Note also my further response in the linked thread:

1 Like

Using the KeePass client, Cadaver, etc I can confirm the WebDav plugin is working as needed. So the KeeWeb webapp not working is an issue on their side. I will open a ticket with them, the server appears to be working as expected. Thank you for helping me confirm.

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