title: 403 Forbidden on CORS request from local
body: <p>I am working with <a href="https://www.gluu.org/" rel="nofollow noreferrer">Gluu Server</a> and trying to get the OpenID Connect configuration from the <code>/.well-known/openid-configuration</code> endpoint through a CORS/AJAX request (for use with an Angular app). However, when I try to request the endpoint from a locally hosted app/HTML file with XHR requesting the endpoint, I receive a <code>403 Forbidden</code> error.</p>
  
  <p>This only seems to happen when the request stems from a local context, i.e. Angular's development server or a local HTML file requesting the endpoint. If I open the same HTML file that performs the AJAX request, hosted on a server, it works.</p>
  
  <p>The testing HTML file looks like the following</p>
  
  <pre class="lang-html prettyprint-override"><code>&lt;!DOCTYPE html&gt;
  &lt;html&gt;
  &lt;head&gt;
      &lt;meta charset="utf-8"&gt;
      &lt;meta http-equiv="X-UA-Compatible" content="IE=edge"&gt;
      &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
  &lt;/head&gt;
  &lt;body&gt;
      &lt;div id="content"&gt;&lt;/div&gt;
      &lt;script type="text/javascript"&gt;
          var url = 'https://example.com/.well-known/openid-configuration';
  
          var req = new XMLHttpRequest();
  
          req.open('GET', url, true);
          req.setRequestHeader('Content-Type', 'application/json');
  
          req.onload = () =&gt; {
              if (req.status &gt;= 200 &amp;&amp; req.status &lt; 400) {
                  console.log('[XHR SUCCESS]');
                  var el = document.getElementById('content');
                  el.innerHTML = req.responseText;
              } else {
                  console.log('[XHR ERROR]', req);
              }
          }
  
          req.onerror = () =&gt; {
              console.log('[XHR CONNECTION ERROR]');
          }
          req.send();
      &lt;/script&gt;
  &lt;/body&gt;
  &lt;/html&gt;
  </code></pre>
  
  <h2>Requesting from local file</h2>
  
  <p>As mentioned above, when requesting from a local HTML file, I receive the <code>403 Forbidden</code> error.</p>
  
  <p>In the browser console (Chrome), two errors are output:</p>
  
  <pre><code>Failed to load resource: the server responded with a status of 403 (Forbidden)
  </code></pre>
  
  <pre><code>Access to XMLHttpRequest at 'https://example.com/.well-known/openid-configuration' from origin 'null' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
  </code></pre>
  
  <p>The only output on the server, that I have found, relating to this, is in the file <code>/var/log/apache2/other_vhosts_access.log</code>:</p>
  
  <pre><code>example.com:443 &lt;IP&gt; - - [11/Mar/2019:10:45:20 +0000] "OPTIONS /.well-known/openid-configuration HTTP/1.1" 403 3763 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36"
  </code></pre>
  
  <p>The server receives the following (from the <code>log_forensic</code> module for Apache) when requested from local:</p>
  
  <pre><code>OPTIONS /.well-known/openid-configuration HTTP/1.1|Host:example.com|Connection:keep-alive|Pragma:no-cache|Cache-Control:no-cache|Access-Control-Request-Method:GET|Origin:null|User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36|Access-Control-Request-Headers:content-type|Accept:*/*|Accept-Encoding:gzip, deflate, br|Accept-Language:en-US,en;q=0.9
  </code></pre>
  
  <h2>Requesting from server-hosted file</h2>
  
  <p>When doing the exact same thing as above, but with the HTML file hosted on a server, the request completes successfully.</p>
  
  <p>Output in the access log:</p>
  
  <pre><code>example.com:443 &lt;IP&gt; - - [11/Mar/2019:11:06:46 +0000] "OPTIONS /.well-known/openid-configuration HTTP/1.1" 200 779 "http://example.org/xhr-cors.html" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36"
  example.com:443 &lt;IP&gt; - - [11/Mar/2019:11:06:46 +0000] "GET /.well-known/openid-configuration HTTP/1.1" 200 6629 "http://example.org/xhr-cors.html" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36"
  </code></pre>
  
  <p>From <code>log_forensic</code>:</p>
  
  <pre><code>OPTIONS /.well-known/openid-configuration HTTP/1.1|Host:example.com|Connection:keep-alive|Access-Control-Request-Method:GET|Origin:http%3a//example.org|User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36|Access-Control-Request-Headers:content-type|Accept:*/*|Referer:http%3a//example.org/xhr-cors.html|Accept-Encoding:gzip, deflate, br|Accept-Language:en-US,en;q=0.9
  
  GET /.well-known/openid-configuration HTTP/1.1|Host:example.com|Connection:keep-alive|Origin:http%3a//example.org|User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36|Content-Type:application/json|Accept:*/*|Referer:http%3a//example.org/xhr-cors.html|Accept-Encoding:gzip, deflate, br|Accept-Language:en-US,en;q=0.9
  </code></pre>
  
  <h2>Apache configuration</h2>
  
  <p>The configuration for Apache on the server is</p>
  
  <pre><code>&lt;VirtualHost  *:80&gt;
          ServerName example.com
          Redirect  / https://example.com/
          DocumentRoot "/var/www/html/"
  RewriteEngine on
  RewriteCond %{SERVER_NAME} =example.com
  RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
  &lt;/VirtualHost&gt;
  
  &lt;VirtualHost *:443&gt;
          DocumentRoot "/var/www/html/"
          ServerName example.com:443
  
          LogLevel warn
          SSLEngine on
          SSLProtocol -all +TLSv1.1 +TLSv1.2
          SSLHonorCipherOrder On
          SSLCipherSuite ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:DHE-RSA-AES128-SHA256:DHE-DSS-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:DHE-RSA-AES128-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK
  
  #               SetEnv proxy-nokeepalive 1
          SetEnv proxy-initial-not-pooled 1
          Timeout 60
                  ProxyTimeout 60
  
          # Security headers
  #        Header always append X-Frame-Options SAMEORIGIN
                  Header always set X-Xss-Protection "1; mode=block"
                  Header always set X-Content-Type-Options nosniff
  #        Header always set Content-Security-Policy "default-src 'self' 'unsafe-inline' https://example.com"
          Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
  
          Header edit Set-Cookie ^((?!session_state).*)$ $1;HttpOnly
          SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0
  
                  # Unset X-ClientCert to make sure that we not get certificate in request
          RequestHeader unset X-ClientCert
  
                  # Turn off support for true Proxy behaviour as we are acting as a transparent proxy
          ProxyRequests Off
  
                  # Turn off VIA header as we know where the requests are proxied
          ProxyVia Off
  
                  # Turn on Host header preservation so that the servlet container
                  # can write links with the correct host and rewriting can be avoided.
          ProxyPreserveHost On
  
                  # Preserve the scheme when proxying the request to Jetty
                  RequestHeader set X-Forwarded-Proto "https" env=HTTPS
  
                  Header unset ETag
          FileETag None
  
          RedirectMatch ^(/)$ /identity/
  
                  # Set the permissions for the proxy
                  &lt;Proxy *&gt;
                    AddDefaultCharset off
                    Order deny,allow
                    Allow from all
                  &lt;/Proxy&gt;
  
          &lt;Location /oxauth&gt;
                  ProxyPass http://localhost:8081/oxauth retry=5 connectiontimeout=60 timeout=60
  #                Header set Access-Control-Allow-Origin "*"
                  Order deny,allow
                  Allow from all
          &lt;/Location&gt;
  
          &lt;LocationMatch /oxauth/auth/cert/cert-login&gt;
              SSLVerifyClient optional_no_ca
              SSLVerifyDepth 10
              SSLOptions -StdEnvVars +ExportCertData
  
                          # Forward certificate to destination server
              RequestHeader set X-ClientCert %{SSL_CLIENT_CERT}s
          &lt;/LocationMatch&gt;
  
          &lt;Location /idp&gt;
                  ProxyPass http://localhost:8086/idp retry=5 connectiontimeout=60 timeout=60
                  Order deny,allow
                  Allow from all
          &lt;/Location&gt;
  
          &lt;Location /identity&gt;
                  ProxyPass http://localhost:8082/identity retry=5 connectiontimeout=60 timeout=60
                  Order deny,allow
                  Allow from all
          &lt;/Location&gt;
  
          &lt;Location /cas&gt;
                  ProxyPass http://localhost:8083/cas retry=5 connectiontimeout=60 timeout=60
                  Order deny,allow
                  Allow from all
          &lt;/Location&gt;
  
          &lt;Location /oxauth-rp&gt;
                  ProxyPass http://localhost:8085/oxauth-rp retry=5 connectiontimeout=60 timeout=60
                  Order deny,allow
                  Allow from all
          &lt;/Location&gt;
  
          &lt;Location /asimba&gt;
                  ProxyPass http://localhost:8084/asimba retry=5 connectiontimeout=60 timeout=60
                  Order deny,allow
                  Allow from all
          &lt;/Location&gt;
  
          &lt;Location /passport&gt;
                  ProxyPass http://localhost:8090/passport retry=5 connectiontimeout=60 timeout=60
                  Order deny,allow
                  Allow from all
          &lt;/Location&gt;
  
          &lt;Location /casa&gt;
                  ProxyPass http://localhost:8091/casa retry=5 connectiontimeout=60 timeout=60
                  Order deny,allow
                  Allow from all
          &lt;/Location&gt;
  
         &lt;LocationMatch "/.well-known/openid-configuration"&gt;
                 ProxyPass http://localhost:8081/oxauth/.well-known/openid-configuration
                 Header set Access-Control-Allow-Origin "*"
         &lt;/LocationMatch&gt;
  
  #                        ProxyPass /.well-known/openid-configuration http://localhost:8081/oxauth/.well-known/openid-configuration
  ProxyPass /.well-known/simple-web-discovery http://localhost:8081/oxauth/.well-known/simple-web-discovery
  ProxyPass        /.well-known/webfinger http://localhost:8081/oxauth/.well-known/webfinger
          ProxyPass        /.well-known/uma2-configuration http://localhost:8081/oxauth/restv1/uma2-configuration
          ProxyPass        /.well-known/fido-configuration http://localhost:8081/oxauth/restv1/fido-configuration
          ProxyPass        /.well-known/fido-u2f-configuration http://localhost:8081/oxauth/restv1/fido-configuration
          ProxyPass        /.well-known/scim-configuration http://localhost:8082/identity/restv1/scim-configuration
          ServerAlias        example.com
          SSLCertificateFile        /etc/letsencrypt/live/example.com/fullchain.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
  Include /etc/letsencrypt/options-ssl-apache.conf
  &lt;/VirtualHost&gt;
  </code></pre>
  
  <p>I have commented out the</p>
  
  <pre><code>ProxyPass /.well-known/openid-configuration http://localhost:8081/oxauth/.well-known/openid-configuration
  </code></pre>
  
  <p>directive and introduced the</p>
  
  <pre><code>&lt;LocationMatch "/.well-known/openid-configuration"&gt;
          ProxyPass http://localhost:8081/oxauth/.well-known/openid-configuration
          Header set Access-Control-Allow-Origin "*"
  &lt;/LocationMatch&gt;
  </code></pre>
  
  <p>directive to add CORS header(s).</p>
  
  <h2>Other</h2>
  
  <p>Other things I've tried to figure out what the issue is:</p>
  
  <ul>
  <li>Performed a <code>GET</code> request to the endpoint through Postman, which completed successfully.</li>
  <li>Performed an <code>OPTIONS</code> request to the endpoint through Postman, which completed successfully.</li>
  </ul>
  
  <p>I'd very much appreciate some input on this as it has me pretty stumped and being unable to work from local when developing is cumbersome. If any clarification is needed, please let me know.</p>