Server is in my local network, serving HTTP
(not HTTPS
). It uses libraries express
, cors
, bodyparser
.
Passing web client is on Chrome macOS version 135.0.7049.85
.
Failing web client is on Chrome iOS version 135.0.7049.83
.
I've confirmed that CORS is not the issue, and I can see server logs accepting the request and returning response.
I tried disabling "safe browsing" in Chrome settings, no change.
I tried replacing $.ajax
with fetch
, no change.
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
async function f1() {
console.log('before request')
const res_subs = await $.ajax({
method: 'POST',
url: '/db',
data: {
endpoint: 'endpoint1',
value: 'one two',
language: 'abc'
}
})
console.log('after request')
}
fetch
async function f1() {
console.log('before request')
const res = await fetch(
'/db',
{
method: 'POST',
body: JSON.stringify({
endpoint: 'endpoint1',
value: 'one two',
language: 'abc'
}),
headers: [
['Content-Type', 'application/json']
]
}
)
if (!res.ok) {
throw new Error(res.statusText)
}
console.log('after request')
}
before request
after request
is never printed.
const express = require('express')
const bodyparser = require('body-parser')
const cors = require('cors')
// cross origin request origins
const origins = [
'http://localhost', 'http://127.0.0.1', // local testing (same device)
'http://<server-local-ip-addr>', // local testing (different device)
// etc
]
// handle POST request data with bodyparser
server.use(bodyparser.json())
server.use(bodyparser.urlencoded({
extended: false,
limit: '50mb'
}))
server.use(cors({
origin: function (origin, callback) {
if (origin != null && origins.indexOf(origin) == -1) {
log.error(`cross origin request failed for ${origin}`)
return callback(new Error('CORS for origin ' + origin + ' is not allowed access.'), false)
}
else {
return callback(null, true)
}
}
}))
server.set('port', 80)
/**
*
* @param {string} endpoint
* @param {object} args
* @param {import('express').Response} res
*/
function handle_db(endpoint, args, res) {
// converts request to a database query, returns result
log.debug(`res.length=${data.length}`, ctx)
res.json(data)
res.on('finish', () => { log.debug('res.finish', ctx) })
}
//expose database
server
.route('/db')
.post(function (req, res) {
handle_db(
req.body.endpoint, // db api endpoint
req.body, // other arguments
res
)
})
db_server.main.79.info: database connected successfully
...
server.handle_db.97.debug: res.length=13
server.handle_db.99.debug: res.finish
OPTIONS
handling confirmationBelow is default OPTIONS
preflight response from express
server with cors
middleware.
$ curl -X OPTIONS http://localhost/db -i
HTTP/1.1 204 No Content
X-Powered-By: Express
Vary: Origin, Access-Control-Request-Headers
Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE
Content-Length: 0
Date: Sun, 13 Apr 2025 02:19:50 GMT
Connection: keep-alive
Keep-Alive: timeout=5
It's solved. Eventually I arranged a mobile emulator in order to access web inspector, and saw that actual error was an unrelated logging error inside of the client query method. It was surfaced as unhandled Promise rejection, which I think is because the innermost catch
included a logger method that was also failing.
A mistake in my logger assumed structure of the error call stack that was different between browsers, hence the inconsistent behavior.