Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

estdigital's avatar

CORS Access-Control-Allow-Origin issues on Safari mobile and desktop

We are having some issues with mobile Safari and desktop with enabling CORS. On other browsers it works just fine. We are using a VueJS SPA which interacts with a Laravel API on the same domain but with a different subdomain like so, spa.com -> api.spa.com. When the SPA is booting up it makes a init(first on created) request to the API. This fails as described on Safari.

We're getting the following error messages in the console:

Origin .... is not allowed by Access-Control-Allow-Origin
XMLHttpRequest cannot load .... due to access control checks.
Failed to load resource: Origin .... is not allowed by Access-Control-Allow-Origin

The weird part is that when we refresh the page after it failed it works and is able to make the first init request. So maby not CORS?! Other weird thing is that this bug only arrises on our production environment, in staging and or test it works just fine with the same Safari browsers used. Even some older versions(not most recent) of Safari work on production. Very frustrating..

In Laravel we use the barryvdh/laravel-cors package for controlling CORS settings, in VueJS we use the Axios library:

'supportsCredentials' => true,
'allowedOrigins' => ['*'],
'allowedHeaders' => ['Origin', '*'],
'allowedMethods' => ['*'],
'exposedHeaders' => ['Authorization'],
'maxAge' => 0,

I tried a few different settings like, * or adding Origin, with no result.

export const HTTP = axios.create({
    baseURL: (configService.get('baseURL')),
    headers: {
        'Accept': 'application/json',
        'X-Requested-With': 'XMLHttpRequest',
    },
    withCredentials: true,
    xsrfCookieName: 'XSRF-TOKEN',
    xsrfHeaderName: 'X-XSRF-TOKEN',
    timeout: 15000
});

We added axios-retry to make sure if it failed due to some random bug, it can make the request again but also this fails.

We are about to start pulling our hair out.

Any insights as to why our CORS setup is failing for the latest Safari would be appreciated.

Thanks

0 likes
5 replies
lostdreamer_nl's avatar

If it is only in your production environment, than you've already pretty much isolated it to (web) server / proxy settings, or just maybe the browser settings on both your test devices.

In desktop safari, can you see the options request going out to the server? Can you see / post the response of that request? Are all cors headers there?

Is you server on AWS / or some other hosting party ? I know that a lot of cloud hosting dont allow for "allowedHeaders: *" so you'd have to name every header you want to send through, some browsers are more picky about a missing header.

You might want to compare the 2 request / response headers of the OPTIONS requests of a working chrome vs non working opera.

estdigital's avatar

Thanks it sure looks like indeed a server related bug, we are still looking into this, we have some new insights though:

The OPTIONS pre-flight request headers in Chrome (in a working Safari browser it just shows one GET request in the network tab, cant find any OPTIONS requests):

:authority: ...
:method: OPTIONS
:path: /api/v1/portal/afm/nl
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: nl-NL,nl;q=0.9,en-US;q=0.8,en;q=0.7
access-control-request-headers: x-requested-with,x-xsrf-token
access-control-request-method: GET
cache-control: no-cache
dnt: 1
origin: https://.....
pragma: no-cache
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36

The response headers on that request:

access-control-allow-credentials: true
access-control-allow-headers: X-REQUESTED-WITH,X-XSRF-TOKEN
access-control-allow-methods: GET
access-control-allow-origin: https://.....
cache-control: no-cache, private
content-length: 0
content-type: text/html; charset=UTF-8
date: Tue, 18 Dec 2018 22:09:33 GMT
server: Apache/2
status: 200
vary: User-Agent
x-powered-by: PHP/7.1.24

Chrome is also affected on mobile iOS. It still looks like a server related bug, when we "Disable Cross-Origin Restrictions" in Safari develop mode we get the following error:

Unhandled Promise Rejection: <!DOCTYPE HTML PUBLIC “-//IETF//DTD HTML 2.0//EN”>
<html><head>
<title>421 Misdirected Request</title>
</head><body>
<h1>Misdirected Request</h1>
<p>The client needs a new connection for this
request as the requested host name does not match
the Server Name Indication (SNI) in use for this
connection.</p>
</body></html>

Sometimes on Chrome(in iOS) this error gets displayed when requesting the SPA(so it does not work/load at all). We made a 'sort' of fix which wait for a couple of seconds and then retries the call to the API again, this works but the resources, images, fonts etc of the SPA are broken because they get the same 421 HTTP status code error on loading the resource.

We checked our server errors and this displays the same error something about SNI:

AH02032: Hostname .... provided via SNI and hostname ... provided via HTTP have no compatible SSL setup, referer: ....
lostdreamer_nl's avatar
Level 53

on the SNI front I've never had any exceptions (never heard of it actually) but google tells me this would probably be because of a combination of

  • HTTP/2 capable browsers
  • multiple domain certificate (probably a wildcard cert)
  • at some point apache wants to renegotiate the TLS which is not supported in the client implementation of http/2

It could also be that there is a reverse proxy server between your actual server and the client, and the server is not sending a (or sending a wrong) 'Host' header to the client.

But seeing the headers and the fact that it's not all browsers my guess is on the combination of HTTP 2 connection + Apache + multi / wildcard domain certificate.

If you current setup is like that, the easiest fix would be to setup a separate certificate for the subdomains instead of the current wildcard one.

1 like
estdigital's avatar

@LOSTDREAMER_NL - This was indeed the case, we temp switched the wildcard certificate for letsencrypt with that it started working, after this conclusion we setup a separate certificate for both of the subdomains, issues fixed! Thanks.

Please or to participate in this conversation.