For background context see here
I am attempting to clone a repository on Codeberg using the isomorphic-git library, which is a pure-JavaScript git client which is able to run in a web browser. I am including the library in a custom test app which is hosted by the webpack dev server on http://127.0.0.1:8080/
When I try to clone a repository, I find that it is blocked by CORS:
Screen Shot of CORS error
Indeed, if I try to copy the request as cURL and perform it manually, I see:
$ curl --output - -v 'https://codeberg.org/ArcaneNibble/test2/info/refs?service=git-upload-pack' \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:151.0) Gecko/20100101 Firefox/151.0' \
-H 'Accept: */*' \
-H 'Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh-HK;q=0.7,zh;q=0.6' \
-H 'Accept-Encoding: gzip, deflate, br, zstd' \
-H 'Referer: http://127.0.0.1:8080/' \
-H 'Origin: http://127.0.0.1:8080' \
-H 'DNT: 1' \
-H 'Sec-GPC: 1' \
-H 'Connection: keep-alive' \
-H 'Sec-Fetch-Dest: empty' \
-H 'Sec-Fetch-Mode: cors' \
-H 'Sec-Fetch-Site: cross-site' \
-H 'Priority: u=4' \
-H 'TE: trailers'
* Trying 2a0a:4580:103f:c0de::1:443...
* Connected to codeberg.org (2a0a:4580:103f:c0de::1) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /Users/rqou/code/emsdk/python/3.9.2_64bit/lib/python3.9/site-packages/certifi/cacert.pem
* CApath: none
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=*.codeberg.org
* start date: Apr 15 11:09:01 2026 GMT
* expire date: Jul 14 11:09:00 2026 GMT
* subjectAltName: host "codeberg.org" matched cert's "codeberg.org"
* issuer: C=US; O=Let's Encrypt; CN=E8
* SSL certificate verify ok.
* Using HTTP2, server supports multiplexing
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x12d811200)
> GET /ArcaneNibble/test2/info/refs?service=git-upload-pack HTTP/2
> Host: codeberg.org
> user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:151.0) Gecko/20100101 Firefox/151.0
> accept: */*
> accept-language: en-US,en;q=0.9,zh-CN;q=0.8,zh-HK;q=0.7,zh;q=0.6
> accept-encoding: gzip, deflate, br, zstd
> referer: http://127.0.0.1:8080/
> origin: http://127.0.0.1:8080
> dnt: 1
> sec-gpc: 1
> connection: keep-alive
> sec-fetch-dest: empty
> sec-fetch-mode: cors
> sec-fetch-site: cross-site
> priority: u=4
> te: trailers
>
< HTTP/2 200
< cache-control: no-cache, max-age=0, must-revalidate
< content-type: application/x-git-upload-pack-advertisement
< expires: 1980年1月01日 00:00:00 GMT
< pragma: no-cache
< x-frame-options: SAMEORIGIN
< content-length: 425
< date: 2026年6月09日 21:07:06 GMT
< x-server-name: s_forgejo_secondary_alpspitz
< strict-transport-security: max-age=63072000; includeSubDomains; preload;
< permissions-policy: interest-cohort=()
< alt-svc: h3=":443";ma=2592000;
<
001e# service=git-upload-pack
00000146d5ebd35b093ad1597f3d4d40988466aa2ce667de HEADmulti_ack thin-pack side-band side-band-64k ofs-delta shallow deepen-since deepen-not deepen-relative no-progress include-tag multi_ack_detailed allow-tip-sha1-in-want allow-reachable-sha1-in-want no-done symref=HEAD:refs/heads/main filter object-format=sha1 agent=git/2.47.3
003dd5ebd35b093ad1597f3d4d40988466aa2ce667de refs/heads/main
* Connection #0 to host codeberg.org left intact
There are indeed no CORS headers in the response.
If we allow the use of parallel universes where I have implemented a CORS proxy, the subsequent request that would be made would be:
$ curl --output - -v 'https://codeberg.org/ArcaneNibble/test2/git-upload-pack' \
-X POST \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:151.0) Gecko/20100101 Firefox/151.0' \
-H 'Accept: application/x-git-upload-pack-result' \
-H 'Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh-HK;q=0.7,zh;q=0.6' \
-H 'Accept-Encoding: gzip, deflate, br, zstd' \
-H 'Referer: http://127.0.0.1:8080/' \
-H 'content-type: application/x-git-upload-pack-request' \
-H 'Origin: http://127.0.0.1:8080' \
-H 'DNT: 1' \
-H 'Sec-GPC: 1' \
-H 'Connection: keep-alive' \
-H 'Sec-Fetch-Dest: empty' \
-H 'Sec-Fetch-Mode: cors' \
-H 'Sec-Fetch-Site: cross-site' \
-H 'Priority: u=4' \
-H 'TE: trailers' \
--data-raw $'0085want d5ebd35b093ad1597f3d4d40988466aa2ce667de multi_ack_detailed no-done side-band-64k ofs-delta agent=git/isomorphic-git@1.38.4\n000edeepen 10\n00000009done\n'
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 2a0a:4580:103f:c0de::1:443...
* Connected to codeberg.org (2a0a:4580:103f:c0de::1) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /Users/rqou/code/emsdk/python/3.9.2_64bit/lib/python3.9/site-packages/certifi/cacert.pem
* CApath: none
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=*.codeberg.org
* start date: Apr 15 11:09:01 2026 GMT
* expire date: Jul 14 11:09:00 2026 GMT
* subjectAltName: host "codeberg.org" matched cert's "codeberg.org"
* issuer: C=US; O=Let's Encrypt; CN=E8
* SSL certificate verify ok.
* Using HTTP2, server supports multiplexing
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x15a00d400)
> POST /ArcaneNibble/test2/git-upload-pack HTTP/2
> Host: codeberg.org
> user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:151.0) Gecko/20100101 Firefox/151.0
> accept: application/x-git-upload-pack-result
> accept-language: en-US,en;q=0.9,zh-CN;q=0.8,zh-HK;q=0.7,zh;q=0.6
> accept-encoding: gzip, deflate, br, zstd
> referer: http://127.0.0.1:8080/
> content-type: application/x-git-upload-pack-request
> origin: http://127.0.0.1:8080
> dnt: 1
> sec-gpc: 1
> connection: keep-alive
> sec-fetch-dest: empty
> sec-fetch-mode: cors
> sec-fetch-site: cross-site
> priority: u=4
> te: trailers
> content-length: 160
>
* We are completely uploaded and fine
< HTTP/2 200
< cache-control: max-age=0, private, must-revalidate, no-transform
< content-type: application/x-git-upload-pack-result
< x-frame-options: SAMEORIGIN
< content-length: 493
< date: 2026年6月09日 21:11:59 GMT
< x-server-name: s_forgejo_main
< strict-transport-security: max-age=63072000; includeSubDomains; preload;
< permissions-policy: interest-cohort=()
< alt-svc: h3=":443";ma=2592000;
<
00000008NAK
0023Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
0043Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
00e7PACK<binary data omitted>
Again there are no CORS headers.
While attempting to report the issue as an upstream Forgejo issue, I was told that this "definitely" should work, so I'm assuming this is an issue with Codeberg's deployment specifically.
### Comment
For background context see [here](https://codeberg.org/forgejo/forgejo/issues/13026)
I am attempting to clone a repository on Codeberg using the [isomorphic-git](https://isomorphic-git.org/) library, which is a pure-JavaScript git client which is able to run in a web browser. I am including the library in a custom test app which is hosted by the [webpack](https://webpack.js.org/) dev server on `http://127.0.0.1:8080/`
When I try to clone a repository, I find that it is blocked by CORS:

Indeed, if I try to copy the request as cURL and perform it manually, I see:
```
$ curl --output - -v 'https://codeberg.org/ArcaneNibble/test2/info/refs?service=git-upload-pack' \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:151.0) Gecko/20100101 Firefox/151.0' \
-H 'Accept: */*' \
-H 'Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh-HK;q=0.7,zh;q=0.6' \
-H 'Accept-Encoding: gzip, deflate, br, zstd' \
-H 'Referer: http://127.0.0.1:8080/' \
-H 'Origin: http://127.0.0.1:8080' \
-H 'DNT: 1' \
-H 'Sec-GPC: 1' \
-H 'Connection: keep-alive' \
-H 'Sec-Fetch-Dest: empty' \
-H 'Sec-Fetch-Mode: cors' \
-H 'Sec-Fetch-Site: cross-site' \
-H 'Priority: u=4' \
-H 'TE: trailers'
* Trying 2a0a:4580:103f:c0de::1:443...
* Connected to codeberg.org (2a0a:4580:103f:c0de::1) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /Users/rqou/code/emsdk/python/3.9.2_64bit/lib/python3.9/site-packages/certifi/cacert.pem
* CApath: none
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=*.codeberg.org
* start date: Apr 15 11:09:01 2026 GMT
* expire date: Jul 14 11:09:00 2026 GMT
* subjectAltName: host "codeberg.org" matched cert's "codeberg.org"
* issuer: C=US; O=Let's Encrypt; CN=E8
* SSL certificate verify ok.
* Using HTTP2, server supports multiplexing
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x12d811200)
> GET /ArcaneNibble/test2/info/refs?service=git-upload-pack HTTP/2
> Host: codeberg.org
> user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:151.0) Gecko/20100101 Firefox/151.0
> accept: */*
> accept-language: en-US,en;q=0.9,zh-CN;q=0.8,zh-HK;q=0.7,zh;q=0.6
> accept-encoding: gzip, deflate, br, zstd
> referer: http://127.0.0.1:8080/
> origin: http://127.0.0.1:8080
> dnt: 1
> sec-gpc: 1
> connection: keep-alive
> sec-fetch-dest: empty
> sec-fetch-mode: cors
> sec-fetch-site: cross-site
> priority: u=4
> te: trailers
>
< HTTP/2 200
< cache-control: no-cache, max-age=0, must-revalidate
< content-type: application/x-git-upload-pack-advertisement
< expires: 1980年1月01日 00:00:00 GMT
< pragma: no-cache
< x-frame-options: SAMEORIGIN
< content-length: 425
< date: 2026年6月09日 21:07:06 GMT
< x-server-name: s_forgejo_secondary_alpspitz
< strict-transport-security: max-age=63072000; includeSubDomains; preload;
< permissions-policy: interest-cohort=()
< alt-svc: h3=":443";ma=2592000;
<
001e# service=git-upload-pack
00000146d5ebd35b093ad1597f3d4d40988466aa2ce667de HEADmulti_ack thin-pack side-band side-band-64k ofs-delta shallow deepen-since deepen-not deepen-relative no-progress include-tag multi_ack_detailed allow-tip-sha1-in-want allow-reachable-sha1-in-want no-done symref=HEAD:refs/heads/main filter object-format=sha1 agent=git/2.47.3
003dd5ebd35b093ad1597f3d4d40988466aa2ce667de refs/heads/main
* Connection #0 to host codeberg.org left intact
```
There are indeed no CORS headers in the response.
If we allow the use of parallel universes where I have implemented a CORS proxy, the subsequent request that would be made would be:
```
$ curl --output - -v 'https://codeberg.org/ArcaneNibble/test2/git-upload-pack' \
-X POST \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:151.0) Gecko/20100101 Firefox/151.0' \
-H 'Accept: application/x-git-upload-pack-result' \
-H 'Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh-HK;q=0.7,zh;q=0.6' \
-H 'Accept-Encoding: gzip, deflate, br, zstd' \
-H 'Referer: http://127.0.0.1:8080/' \
-H 'content-type: application/x-git-upload-pack-request' \
-H 'Origin: http://127.0.0.1:8080' \
-H 'DNT: 1' \
-H 'Sec-GPC: 1' \
-H 'Connection: keep-alive' \
-H 'Sec-Fetch-Dest: empty' \
-H 'Sec-Fetch-Mode: cors' \
-H 'Sec-Fetch-Site: cross-site' \
-H 'Priority: u=4' \
-H 'TE: trailers' \
--data-raw $'0085want d5ebd35b093ad1597f3d4d40988466aa2ce667de multi_ack_detailed no-done side-band-64k ofs-delta agent=git/isomorphic-git@1.38.4\n000edeepen 10\n00000009done\n'
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 2a0a:4580:103f:c0de::1:443...
* Connected to codeberg.org (2a0a:4580:103f:c0de::1) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /Users/rqou/code/emsdk/python/3.9.2_64bit/lib/python3.9/site-packages/certifi/cacert.pem
* CApath: none
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=*.codeberg.org
* start date: Apr 15 11:09:01 2026 GMT
* expire date: Jul 14 11:09:00 2026 GMT
* subjectAltName: host "codeberg.org" matched cert's "codeberg.org"
* issuer: C=US; O=Let's Encrypt; CN=E8
* SSL certificate verify ok.
* Using HTTP2, server supports multiplexing
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x15a00d400)
> POST /ArcaneNibble/test2/git-upload-pack HTTP/2
> Host: codeberg.org
> user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:151.0) Gecko/20100101 Firefox/151.0
> accept: application/x-git-upload-pack-result
> accept-language: en-US,en;q=0.9,zh-CN;q=0.8,zh-HK;q=0.7,zh;q=0.6
> accept-encoding: gzip, deflate, br, zstd
> referer: http://127.0.0.1:8080/
> content-type: application/x-git-upload-pack-request
> origin: http://127.0.0.1:8080
> dnt: 1
> sec-gpc: 1
> connection: keep-alive
> sec-fetch-dest: empty
> sec-fetch-mode: cors
> sec-fetch-site: cross-site
> priority: u=4
> te: trailers
> content-length: 160
>
* We are completely uploaded and fine
< HTTP/2 200
< cache-control: max-age=0, private, must-revalidate, no-transform
< content-type: application/x-git-upload-pack-result
< x-frame-options: SAMEORIGIN
< content-length: 493
< date: 2026年6月09日 21:11:59 GMT
< x-server-name: s_forgejo_main
< strict-transport-security: max-age=63072000; includeSubDomains; preload;
< permissions-policy: interest-cohort=()
< alt-svc: h3=":443";ma=2592000;
<
00000008NAK
0023Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
0043Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
00e7PACK<binary data omitted>
```
Again there are no CORS headers.
While attempting to report the issue as an upstream Forgejo issue, I was told that this "definitely" should work, so I'm assuming this is an issue with Codeberg's deployment specifically.