Skip to main content
Redhat Developers  Logo
  • Products

    Featured

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat OpenShift AI
      Red Hat OpenShift AI
    • Red Hat Enterprise Linux AI
      Linux icon inside of a brain
    • Image mode for Red Hat Enterprise Linux
      RHEL image mode
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • Red Hat Developer Hub
      Developer Hub
    • View All Red Hat Products
    • Linux

      • Red Hat Enterprise Linux
      • Image mode for Red Hat Enterprise Linux
      • Red Hat Universal Base Images (UBI)
    • Java runtimes & frameworks

      • JBoss Enterprise Application Platform
      • Red Hat build of OpenJDK
    • Kubernetes

      • Red Hat OpenShift
      • Microsoft Azure Red Hat OpenShift
      • Red Hat OpenShift Virtualization
      • Red Hat OpenShift Lightspeed
    • Integration & App Connectivity

      • Red Hat Build of Apache Camel
      • Red Hat Service Interconnect
      • Red Hat Connectivity Link
    • AI/ML

      • Red Hat OpenShift AI
      • Red Hat Enterprise Linux AI
    • Automation

      • Red Hat Ansible Automation Platform
      • Red Hat Ansible Lightspeed
    • Developer tools

      • Red Hat Trusted Software Supply Chain
      • Podman Desktop
      • Red Hat OpenShift Dev Spaces
    • Developer Sandbox

      Developer Sandbox
      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Secure Development & Architectures

      • Security
      • Secure coding
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
      • View All Technologies
    • Start exploring in the Developer Sandbox for free

      sandbox graphic
      Try Red Hat's products and technologies without setup or configuration.
    • Try at no cost
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • Java
      Java icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • API Catalog
    • Product Documentation
    • Legacy Documentation
    • Red Hat Learning

      Learning image
      Boost your technical skills to expert-level with the help of interactive lessons offered by various Red Hat Learning programs.
    • Explore Red Hat Learning
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

Conquer CORS errors in OpenShift web applications

Solutions for cross-origin resource sharing

August 29, 2023
Evan Shortiss
Related topics:
JavaKubernetesQuarkus
Related products:
Developer SandboxRed Hat build of QuarkusRed Hat OpenShift

Share:

    In this article, I'll give a brief overview of cross-origin resource sharing (CORS) in the context of modern web applications and their interactions with HTTP APIs. I will also provide a sample solution(hosted on GitHub) for avoiding CORS-related issues in a web application that's served from Red Hat OpenShift.

    The sample solution uses Red Hat's Universal Base Images for Node.js, NGINX, and Java. Instructions are included in this post to help you deploy and test the sample solution for free in the Developer Sandbox for Red Hat OpenShift.

    What exactly is CORS?

    If you're not familiar with CORS or need a refresher, you should take a look at this excellent primer published by Julia Evans on wizardzines.com. To summarize, when JavaScript executing in a web browser on one origin (e.g., https://ui.foo.bar) initiates a request to an endpoint running on another origin (e.g., https://api.foo.bar), it's classed as a cross-origin request. If the target origin of the request doesn't return the appropriate CORS headers to confirm that it supports cross-origin requests, then the browser returns an error to the JavaScript code that invoked the request instead of revealing the contents of the response. In some instances, cross-origin requests also require a preflight HTTP OPTIONS request, which adds overhead to your application!

    You might be wondering why web browsers enforce CORS security measures. Imagine a scenario where you visit a malicious website, and it executes JavaScript that initiates a cross-origin request to your bank's servers. If you had recently visited your bank's website, your web browser might still have a valid session stored, meaning the malicious request could potentially succeed in accessing your banking information. Thankfully, web browsers enforce CORS security measures to prevent this from happening.

    CORS in practice

    Needless to say, there are legitimate reasons for an application running on one origin to request resources from another origin; otherwise you wouldn't be reading this article. For example, a common scenario is that a web application hosted on an origin such as ui.foo.bar needs to request resources from a HTTP API hosted on api.foo.bar. Figure 1 illustrates this using an NGINX web server for the static content and a Quarkus-based Java application serving up an HTTP API.

    A web browser that is requesting static assets from ui.foo.bar, and requesting resources from an HTTP API hosted at api.foo.bar
    Figure 1: A web application communicating with an API using cross-origin requests.

    In Figure 1, the requests from the React web application (ui.foo.bar) to the Quarkus-based HTTP API (api.foo.bar) will only be successful if the appropriate CORS headers are set on the responses returned by the HTTP API. This also assumes that api.foo.bar is hosted at a publicly addressable endpoint and that the team building it is willing to configure it to support CORS.

    Assuming there's no way to return the CORS headers from the HTTP API, a common workaround is to use a CORS proxy, as shown in Figure 2. The CORS proxy will set the appropriate CORS headers, e.g., Access-Control-Allow-Origin: ui.foo.bar before returning responses from the HTTP API to the web browser.

    Using a CORS Proxy to Inject Access-Control Headers
    Figure 2: Using a CORS proxy to inject access-control headers to avoid CORS errors.

    A third solution, and the one I will demonstrate in this post, doesn't require setting any CORS headers and doesn't require a CORS proxy. That's because this solution will result in all requests being classified as same-origin, eliminating CORS entirely! It's possible to implement this solution using path-based routing built into OpenShift Routes, or by using the web server (such as Apache or NGINX) that's used to serve the React application as a reverse proxy to access the HTTP API.

    NGINX reverse proxy solution

    A sample solution is fully documented in this repository on GitHub; I'll provide an overview of the key points here, though. Figure 3 illustrates this solution.

    Using the same NGINX web server that hosts the frontend as reverse proxy to reach a HTTP API.
    Figure 3: Using the NGINX to serve static assets and as a reverse proxy to access an HTTP API.

    The real magic is in the NGINX configuration in the react-ui/nginx.conf file. NGINX serves up the React application, but it also defines a custom location block that proxies HTTP API requests to the Quarkus back end:

    location /hello {
        resolver $NAMESERVERS valid=10s;
        proxy_pass "$API_URL$request_uri";
        proxy_redirect "$API_URL" "";
    }

    The configuration might vary slightly from application to application, but this particular location block instructs NGINX to do the following for all request paths beginning with /hello:

    1. Use the nameservers defined in $NAMESERVERS to resolve hostnames. This is important because the sample solution uses an internal OpenShift service URL, not a public hostname for the back end.
    2. Proxy any request beginning with /hello to an upstream URL defined by the $API_URL variable.
    3. Remove the internal hostname from the Location response header returned by the upstream API server. This ensures that if the API server issues a redirect, the URL for the redirect is pointing at NGINX and not the internal hostname for the Pod.

    Note: You can find more information related to the proxy directives in NGINX's official ngx_http_proxy_module documentation.

    The API_URL and NAMESERVERS variables referenced in the nginx.conf are replaced when the NGINX container starts, as seen in the CMD in the Containerfile for the image. As you might expect, the API_URL is the base URL of the Quarkus HTTP API, and the NAMESERVERS are specified to ensure that NGINX can resolve the internal Service hostname targeted by API_URL.

    As for the React application, the code used to interact with the HTTP API can perform a same-origin request without specifying the hostname:

    const response = await fetch('/hello')

    That's pretty convenient, right? Since the React application can issue same-origin requests to reach the Quarkus HTTP API, there's no need to worry about CORS.

    The following section will guide you through deploying the application to try it out for yourself, and with it deployed you'll be able to explore the path-based routing solution using OpenShift Routes.

    Try it out

    You can deploy the first sample solution on Developer Sandbox for Red Hat OpenShift and experiment with it for free. Follow these steps to deploy the NGINX reverse proxy solution, then continue reading to learn about the path-based solution.

    Log in to Developer Sandbox, then open a terminal using the Web Terminal icon ( >_ ) in the upper-right corner. Issue the following commands in the terminal to clone the source repository and deploy the React-based front end and Quarkus-based back end into your project:

    git clone https://github.com/evanshortiss/cors-nginx-blogpost cors-blog
    cd cors-blog
    
    export NAMESPACE=$(oc project -q)
    export SOURCE_REPOSITORY="https://github.com/evanshortiss/cors-nginx-blogpost"
    
    oc process -f manifests/frontend.yaml -p NAMESPACE=$NAMESPACE -p SOURCE_REPOSITORY=$SOURCE_REPOSITORY | oc apply -f -
    
    oc process -f manifests/backend.yaml -p NAMESPACE=$NAMESPACE -p SOURCE_REPOSITORY=$SOURCE_REPOSITORY | oc apply -f -

    Confirm that both applications are up and running using Topology View from the Developer perspective, as shown in Figure 4.

    The OpenShift Topology View showing NGINX and Quarkus Services running in a Project.
    Figure 4: NGINX and Quarkus deployed on OpenShift as part of the sample solution.

    Click the arrow icon in the top-right of the NGINX item in the Topology View to open the React application in your web browser. The application has two buttons. The first performs a same-origin request to the Quarkus back end via NGINX, while the second performs a cross-origin request directly to the Quarkus backend.

    As you might expect, the same-origin request routed via NGINX will succeed, but the cross-origin request will fail because the Quarkus HTTP API isn't configured to return the necessary CORS headers in its responses. You can use the Developer Tools in your web browser to confirm this, as shown in Figure 5.

    Viewing a CORS Error in Chrome DevTools
    Figure 5: Viewing a successful same-origin request and a failed cross-origin request in Chrome's Developer Tools.

    OpenShift path-based routing solution

    This solution is less flexible and only works if your back end is hosted on OpenShift. The upside is that it doesn't require you to maintain an NGINX proxy configuration. As part of this solution, you'll create two OpenShift routes that share a hostname. The first route will serve the HTML, CSS, and JavaScript by routing traffic to NGINX. The second route will be configured to use the same hostname as the first, but only route traffic that matches a specific URL pattern directly to the Quarkus HTTP API. Figure 5 illustrates the solution that uses OpenShift path-based Routes.

    Two OpenShift Routes using a shared hostname to implement path-based routing.
    Figure 5: Implementing path-based routing using two OpenShift routes that share a hostname.

    Start by deploying the sample application per the instructions in the last section. Next, you'll create a route that directs traffic to the NGINX service that serves the React application. Open the web terminal and create a file named ui-route.yaml that contains the following content:

    kind: Route
    apiVersion: route.openshift.io/v1
    metadata:
      name: example
    spec:
      to:
        kind: Service
        name: react-ui
      tls:
        termination: edge
        insecureEdgeTerminationPolicy: None
      path: /
      port:
        targetPort: 8080-tcp

    Run the following command to create a route to access the React application:

    oc create -f ui-route.yaml

    Obtain the hostname for your new route using the following command:

    oc get route example -o jsonpath='{.spec.host}' | xargs echo

    Create a second file named backend-route.yaml that contains the following content. Make sure you replace the $HOSTNAME placeholder to match the value returned from the oc get route command:

    kind: Route
    apiVersion: route.openshift.io/v1
    metadata:
      name: example-backend
    spec:
      # Note that adding a trailing slash here will require incoming
      # requests to include the trailing slash to match this route.
      # Omit the trailing slash if both /hello and /hello/ should match.
      path: /hello
      # Replace $HOSTNAME with the value you obtained with "oc get route"
      host: $HOSTNAME
      to:
        kind: Service
        name: quarkus-backend
        weight: 100
      port:
        targetPort: 8080
      tls:
        termination: edge
      wildcardPolicy: None

    This route specification uses the same hostname as the route you created to access the React application, but it only matches URLs starting with /hello and routes them to the Quarkus back end. Create the second route by issuing the following command:

    oc create -f backend-route.yaml

    You can test your new path-based configuration is working by visiting the URL returned by this command:

    oc get route example -o jsonpath='https://{.spec.host}' | xargs echo

    Visit the URL, enter a message in the text input, and then use the same-origin button to make a request. Confirm that the request was routed directly to the Quarkus back end using OpenShifts's path-based routing by viewing the Quarkus pod's logs and inspecting the X-Forwarded-For headers using these commands:

    export POD_NAME=$(oc get pods -l 'app=quarkus-backend' -o jsonpath={.items[0].metadata.name})
    
    oc logs $POD_NAME | grep -i 'x-forwarded-for'

    The logs should contain an entry with two IP addresses listed in the X-Forwarded-For header and a more recent entry with a single IP address in the X-Forwarded-For header. The entry that was routed using path-based routing contains just your IP address. The entries routed via NGINX contain both your IP address and the IP address of an OpenShift Router Pod because the previous implementation involves the NGINX forwarding the requests to Quarkus application on behalf of the OpenShift Router. This can be seen in Figure 6.

    Logs from a Quarkus HTTP API Pod showing the differences between the X-Forwarded-For header values when using path-based routing versus the NGINX reverse proxy.
    Figure 6: Logs from a Quarkus HTTP API pod showing the differences between the X-Forwarded-For header values when using path-based routing versus the NGINX reverse proxy.

    Summary

    Dealing with CORS can be tedious, but now you're equipped with the knowledge to work through it. Consider using the techniques discussed here with your applications hosted on OpenShift to avoid CORS entirely. The most important thing is never to get lazy and return Access-Control-Allow-Origin: * from your services unless you're 100% sure that it's the right decision for your application.

    And, of course, be sure to try using the solutions outlined in this article in the Developer Sandbox for Red Hat OpenShift!

    Last updated: September 19, 2023

    Related Posts

    • Learn Quarkus faster in the Developer Sandbox for Red Hat OpenShift

    • Try Camel K in the Developer Sandbox for Red Hat OpenShift

    • Developer Sandbox for Red Hat OpenShift now available on AWS

    • How to migrate your Java applications to Red Hat OpenShift

    • What is the difference between OpenShift and Kubernetes?

    Recent Posts

    • Fly Eagle(3) fly: Faster inference with vLLM & speculative decoding

    • Kafka Monthly Digest: June 2025

    • How to configure and manage Argo CD instances

    • Why Models-as-a-Service architecture is ideal for AI models

    • How to run MicroShift as a container using MINC

    What’s up next?

    Learn how to deploy an application on a cluster using Red Hat OpenShift Service on AWS. This learning path uses a pre-built application to allow you to become more familiar with OpenShift and Kubernetes features.

    Start learning
    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Products

    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform

    Build

    • Developer Sandbox
    • Developer Tools
    • Interactive Tutorials
    • API Catalog

    Quicklinks

    • Learning Resources
    • E-books
    • Cheat Sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site Status Dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2025 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue