Troubleshooting CORS for ORDS with Custom Domains

With around 20 years on the job, Matt is one of the most experienced software developers at Pretius. He likes meeting new people, traveling to conferences, and working on different projects.
He’s also a big sports fan (regularly watches Leeds United, Formula 1, and boxing), and not just as a spectator – he often starts his days on a mountain bike, to tune his mind.
Ok, so you are here because you just like my blogs or you have his error
Access to fetch at 'https://leedsunited.com/ords/hrw/hr/employees/' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
1. The initial CORS error from localhost
This started with a static HTML page served locally at http://127.0.0.1:5500 (e.g. VS Code Live Server … woo! Live Server rocks!), calling an ORDS endpoint at:
https://leedsunited.com/ords/hrw/hr/employees/
In the browser console, the error was:
Access to fetch at 'https://leedsunited.com/ords/hrw/hr/employees/' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
The JavaScript fetch() failed with TypeError: Failed to fetch.
The issue was that the API was reachable, but the browser was refusing to expose the response because the CORS headers were missing. Basically it can access it, but just chucks it in the bin and gives me a nasty red error. It chucks it away because the CORS headers were missing
2. Verifying what the server actually returns
The next step was to confirm what the edge actually returned by using curl -i with an Origin header:
curl -i \
-H "Origin: http://127.0.0.1:5500" \
"https://leedsunited.com/ords/hrw/hr/employees/"
The response looked like this (truncated):
HTTP/2 200
date: ...
content-type: application/json
server: cloudflare
etag: "..."
# no Access-Control-Allow-Origin header here
This told two important things:
ORDS was returning data correctly (HTTP 200 + JSON body).
But there was no
Access-Control-Allow-Originheader at all, so the browser just insta-rejects it … boo.
At this point I thought it was clearly a CORS configuration issue, not a connectivity or ORDS logic issue.
3. Checking ORDS module configuration
I next tried Tim Hall’s approach which is well worth a read.
Its basically allow certain origins to access the rest source module
begin
ords.set_module_origins_allowed(
p_module_name => 'oracle.example.hr',
p_origins_allowed => 'http://localhost:5500');
commit;
end;
/
It should work right?
Nope… I was still getting no CORS headers
HTTP/2 200
date: ...
content-type: application/json
server: cloudflare
etag: "..."
# no Access-Control-Allow-Origin header here
4. Introducing the proxy chain: Cloudflare + Nginx Proxy Manager
If you look closely at my set up. The request path is:
Browser -> Cloudflare (proxied) -> Nginx Proxy Manager -> ORDS
Since ORDS’ headers were not (or not reliably) making it out to the browser, I attempted to add CORS headers at the Nginx Proxy Manager layer, where the public traffic terminates.
In the NPM host for leedsunited.com, under the “Advanced” configuration, the following CORS headers were added:
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Credentials' 'false' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Origin,Content-Type,Accept,Authorization' always;
This is a blanket approach: any origin is allowed (*), and credentials are explicitly disabled. Ask your favorite AI to adjust this to only allow custom Orgins
After saving and letting NPM reload Nginx, the same curl command was run again:
curl -i \
-H "Origin: http://127.0.0.1:5500" \
"https://leedsunited.com/ords/hrw/hr/employees/"
Now the response included the CORS headers - BINGO:
textHTTP/2 200
content-type: application/json
server: cloudflare
access-control-allow-origin: *
access-control-allow-credentials: false
access-control-allow-methods: GET, POST, OPTIONS
access-control-allow-headers: Origin,Content-Type,Accept,Authorization
...
This is exactly what the browser needed to treat the cross-origin response as accessible.
5. Validating the fix in the browser
With the new headers in place, the local HTML app was reloaded at http://127.0.0.1:5500:
The CORS error in the console disappeared.
The
fetch()calls to/employeesand related endpoints now succeeded.The sections populated correctly.
ENJOY
What’s the picture? Western Flatts Cliff Park, Leeds. Visit Yorkshire






