4

Firstly, I'm not sure if this is a Flask or a Safari or a Flask-CORS issue.

In order to emulate a production setting, I have a local Flask backend (API) on one domain and a front-end (SPA) on a different domain. I initially tried this with both the API and the front-end running over HTTPS, using the same self-signed certificate, generated with openssl req -x509 -newkey rsa:4096 -nodes -keyout key.pem -out cert.pem -days 365. But it turns out that the behaviour described below also manifests itself when running on plain HTTP.

Chrome and Firefox work as expected as far as I can see, but when I open the front-end with Safari and navigate around normally, Flask seemingly logs every XHR request twice. I only discovered this, because I have a Flask decorator that increments a counter in the database for specific requests and the counter always increments by 2 rather than 1, if I use Safari.

I found some information in another SO thread, which seems to suggest that Safari is performing a conditional request. The suggestion in the thread is to set If-Unmodified-Since header when making the XHR request in the front-end. I tried that, and that seems to solve the problem — the first GET request seems to be replaced by an OPTIONS request, followed by a GET. If you refresh a couple of times, only the GET is performed. After a brief period of time of refreshing, it performs another OPTIONS request. However, if you don't supply If-Unmodified-Since and log the request headers in the Flask handler, the headers from both requests look identical.

Also, using a different header instead of If-Modified-Since also seems to first fire an OPTIONS request, followed by a GET, but it doesn't prevent the double GET — another GET is fired!

I have set up a Github repo containing a minimal example that reproduces this behaviour. It requires a bit of setup, but the instructions are outlined in the README. There is a screen recording attached as well.

I could settle for just adding If-Unmodified-Since and calling it a day, but it feels a bit like brushing the whole issue under the carpet.. Why is it needed for Safari, but not other browsers?

Karl Sutt
  • 575
  • 3
  • 11
  • Please consider filing a Safari/WebKit bug report about this at https://bugs.webkit.org/enter_bug.cgi?product=WebKit – sideshowbarker Jul 03 '19 at 21:33
  • Thanks @sideshowbarker — I've filed [199492](https://bugs.webkit.org/show_bug.cgi?id=199492). – Karl Sutt Jul 04 '19 at 07:33
  • Thanks — I’ll try to ping some WebKit friends to help get a heads-up to right engineers – sideshowbarker Jul 04 '19 at 07:59
  • Thanks! I think I will try this with another backend, like Ruby + Sinatra or Node, to try to isolate if this really is a Safari issue or not. It really feels like — given the relative popularity of the technologies involved — this would've been uncovered a long time ago, which makes me think I am doing something dumb. – Karl Sutt Jul 04 '19 at 08:10
  • It could be a new problem, or even a regression of an old problem — see for example https://stackoverflow.com/q/56728800/441757 and https://bugs.webkit.org/show_bug.cgi?id=199183. And see https://bugs.webkit.org/show_bug.cgi?id=18972, which is an old bug but is interesting in that it was closed because the cause couldn’t be fixed in the WebKit code anyway but was due to behavior in the platform networking code that WebKit relies on. – sideshowbarker Jul 04 '19 at 08:17
  • Please consider trying to see if you can reproduce this using the Fetch API rather than XHR. I strongly suspect you’ll see the same behavior if you use the Fetch API but if not, that will certainly help isolate the cause. – sideshowbarker Jul 04 '19 at 08:37
  • (Incidentally at this point the XHR API is effectively deprecated and the Fetch API is what you probably almost always want to be using instead in all cases where you can. The only thing you can’t yet do with Fetch in browsers but that you can with XHR is handling/reporting of upload progress.) – sideshowbarker Jul 04 '19 at 08:37
  • Thanks for the suggestion — using Fetch also exhibits the same behaviour, unfortunately. – Karl Sutt Jul 04 '19 at 08:45
  • @sideshowbarker The minimal repro case in the SO question you linked to looks simpler than what I have set up — it seems like CORS does not come into play at all. And it's using Node.js as the backend, which validates that it's not a Flask/Flask-CORS issue. – Karl Sutt Jul 04 '19 at 08:59
  • This bug has now been fixed in the WebKit sources. See https://trac.webkit.org/changeset/247276/webkit. So the fix will likely be available in the next Safari Technology Preview release, and then after that, in the next Safari release — 12.2 or 13, whatever version number it ends up being. – sideshowbarker Jul 09 '19 at 23:10
  • Thanks @sideshowbarker! Yep — I have been following the report and the patches. I'm glad it got fixed. – Karl Sutt Jul 11 '19 at 11:10

0 Answers0