The Performance Cost of CORS Requests on Single-Page Applications
medium.com
medium.com
They should also use just one domain - DNS lookups are a pretty slow and flaky part of the process of loading a webpage, and downloading loads of HTTPS certificates for all the domains involved isn't great either.
Managing just one domain is also a lot easier - less to monitor, less to maintain, simpler architecture, etc.
The only remaining reason for extra domains is to act as a security boundary - for example when you're running untrusted user javascript.
> Nobody has an excuse not to use either HTTP/2 or QUIC on a modern website.
Sure they do. HTTP/2 is not uniformly superior to HTTP/1.1, primarily due to head-of-line blocking; so that on a poor quality network connection, with multiple HTTP/1.1 connections it’s quite possible that one will come through while another takes longer due to packet loss, while on HTTP/2 it’s all on one TCP connection, so that packet loss affects all requests. That’s what QUIC is about fixing. HTTP/2 is also currently incompatible with WebSockets (there’s a draft in progress for resolving that), so HTTP/2 offers no benefit for WebSocket domains or little-to-no benefit for WebSocket-heavy stuff. HTTP/2 also works differently from HTTP/1.1, so that certain moderately obscure combinations of types of long-lived requests can’t be cleanly terminated in HTTP/2 as they can be in HTTP/1.1 (sorry I’m being vague—I can’t recall the details off-hand). And there’s just the usual “existing software that doesn’t support HTTP/2” justification as well.
(Despite the listed criticisms of HTTP/2, I do agree that almost everyone should be using HTTP/2.)
As for QUIC (Google or IETF variant), it’s still experimental, with various unknowns that are being nutted out, and is not suitable for wide deployment by most entities just yet.
> The only remaining reason for extra domains is to act as a security boundary
No, that’s merely one of the best justifications for additional domains. There are other reasons for using other domains, though I would agree with a less strongly worded version of your initial declaration: that they should try, if convenient, to use just one domain.
The implicit assumption being that internally everything trusts the proxy?
Proxying requests to a third-party will mean browser security features like CORS will assume they are entirely safe due to them appearing to come from your domain, and not “gist.github.com” etc.
The point is that when you serverside-proxy requests to third parties, you're implicitly trusting the third party to control the DOM for any app in your domain.
> If you have a slow network, this could be a huge setback, especially when an OPTIONS request takes 2 seconds to respond, and another 2 for the data.
Firstly if your OPTIONS is taking 2 seconds, CORS is not your bottleneck. Even with a half-second PING due to lack of content distribution, and DNS resolution (shouldn't be an issue after the first request), an OPTIONS request should be of negligible size; the bulk of that 2 seconds isn't going to be network latency, that's a serious server-side perf. issue.
Secondly, similarly, if your OPTIONS is taking equally as long as your GET, CORS isn't your bottleneck.
Yes, considering CORS latency is worthwhile if having your API on the same domain is an easy change, but the title overstates the performance cost.
Yes, two seconds and more can absolutely be network latency. If you don't think this is possible nor a regular occurrence for many people, you have been living in a bubble.
Not at all contesting this.
> a regular occurrence for many people
Maybe I am living in a bubble, but this seems unlikely unless your definition of "many" is fairly liberal.
On a 2G connection you might get up to 1000ms latency, if you're in a remote part of the world could could add an extra 500ms or more due to geography. DNS lookup, TLS handshake, and the data transfer, you could potentially get up to 3+ seconds adding it all up. But—apart from aspects of that being one-off, as I mentioned above—generally speaking the whole lot is an example of an extreme. Your average user really shouldn't be seeing that.
If the latter, where are you getting 15-10 second figures from?
In a world with more than 7 billion human beings and a large percentage of them being users of the internet, it doesn’t matter if we are talking about 1% or even only 0.01% of users. Any percentage will include many people.
It might only be many actual users if you’re Google or Facebook, though.
Fine grain control over the options response, in certain contexts, is great. Being able to immediately ship 3rd party functionality and know that it will work is great. A unified API for your JS, your 3rd party JS, and all the API consumers out there, is also great. For me it's a security and speed-of-development thing; not a performance-while-travelling thing. This means it's great for full-featured apps used by businesses in Canada, the US, Japan, and most of Europe.
If a connection is seconds slow; and you have a project that needs to be a complex SPA, the right way of handling slow connections is to fallback to a simple server side pure HTML / CSS bare bones website that sets aggressive caches with crazy secure HTTP headers and uses encrypted cookies to handle session between a separate web server that communicates through the main backend API on the session's behalf. If you use Ember, this is trivial with Fastboot[0] if you've followed best practices, and not much work if you haven't.
Also, how you architect your API and frontend matters greatly. The reason I like following JSON API[1] is that EmberData-like data model mappers / persistence libraries mean that I can supply multitudes more information about the world in one response if I want to. Sure I have multiplexed HTTP2 set up, but if A needs B and B needs C1 through C20 and they each need their own D it's nice to be able to just send that all down and skip all the requests; and of course it's always possible to design the API to be optionally told to fetch only a subset and everything works easily for out of the box because these frontend libraries can rely on the standard.
The only thing I wish came with CORS was a small DSL, so I could do things like whitelist a pattern. But I get it; someone would probably screw up at some point and for what? It's kinda nice that a technology defaulted to secure for once.
[0] https://www.ember-fastboot.com/docs/user-guide
[1] I use v1.1 because I like to stay ahead wherever I can.
This is a very neat trick because you can definitely ask an API for JSON via accept header (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Ac...) while being compliant with simple requests by using query strings and/or multipart/form for routes option.
At this point when I see the Algolia search box on a site, I know I won't get a full search experience it will be slightly crippled with a custom interface and a forced auto-complete with JS only interface support.
An example of this is vuejs.org vs developer.mozilla.org. With Mozilla dev site, I have 'mdn' as my search key (both Firefox and Chrome) and I can type "mdn grid-template-columns" and I get results. With vuejs.org and Algolia, I have to go through multiple clicks and visual hunt and peck to find what I am looking for. It's a productivity killer.
This is a good example why computer experience of today is so terrible:
"Never run a changing system" is dead. Systems are changing all the time.
I'm wondering whether the http://hn.algolia.com/opensearch.xml configuration could help here, might need to tweak it a bit.
(Algolia charges thousands of dollars for some of their services and I find it the worst search system currently used on popular sites.)
Not badmouthing an earlier poster but statements like... "It is also very easy in Kubernetes via Ingress resource." No... this is the opposite of 'very easy'.
Yes, 20+ years of this madness too.
But yeah, that's kind of the point of running a cluster. One thing to look after. What would you rather be running?
HN is a community. You don't have to use your real name, but users need to have some consistent identity for others to relate to. Otherwise we may as well have no usernames and no community at all. That would be a different kind of forum. Anonymity is fine, and throwaways for a specific purpose are ok, just not routinely.
https://hn.algolia.com/?sort=byDate&dateRange=all&type=comme...
- Much better tooling & testing
- Excellent editors like VSCode
- Javascript ES6-7 (and the renewed energy of the language)
- GPU accelerated CSS transitions and effects
- Web assembly
- Modern component frameworks & state management
- So many APIs to cover things you used to have to do yourself: payment, transactional email, authentication
- More expressive APIs like GraphQL
- No real need to maintain infrastructure: publish to CDN services; consume APIs.
Compared to development on [favorite-platform-here] it may not be as seamless, but that's an unfair comparison. You have to compare it other forms of cross-platform development, and there the web really shines. It certainly has its flaws and gaps, but I love what's currently happening in the space.
I've used https://github.com/jpillora/xdomain to get around this and thought it was a thoroughly excellent workaround. There are other libraries that do this too (and according to the GH page, Google use the same approach for their APIs).
All requests are channelled through an iframe on the domain you want to make requests to; then the iframe publishes a policy for which requests are allowed to go through.
- No CORS issues.
- Data can be pushed to clients in real-time (this goes well with reactive UIs).
- No need to send headers with every call/response.
- The server is aware of the client's presence (and can detect the exact moment when they go offline).
WebSockets add a few extra challenges related to recovering from lost connections and rate-limiting but a lot of libraries and frameworks have already solved these problems.
The main reason why HTTP might stick around for a very long time in the future is because HTTP REST APIs are extremely widespread and well understood.
With WS you can’t leverage a CDN hosted solution and instead would need some complicated CDN that is aware of state.
I like HTTP because it just works, especially behind buggy corporate and mobile network firewalls.
CORS is needed for GET if the request needs to send headers like `Authorization`, AFAIK
https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_...
You’ve been able to do that for many years with server-sent events.
> - The server is aware of the client's presence (and can detect the exact moment when they go offline).
You are overly optimistic in saying “exact moment”. You can probably detect when they probably go offline to within a minute, depending on various configuration factors, but they could then immediately come online again anyway.
This much being said: JMAP (https://jmap.io/), an RPC and object syncing protocol sponsored by FastMail and approaching finalisation in the IETF, has used regular HTTP and server-sent events, but one of us has also been prototyping and writing a draft for JMAP over WebSockets; see https://datatracker.ietf.org/doc/draft-murchison-jmap-websoc..., the introduction of which also lists two more key benefits of using a persistent connection for the transport: authentication only needs to be done once, and that request bodies can be compressed too. (In conjunction with the whole HTTP/2-and-WebSocket incompatibility matter, I initially informally opposed the effort as needless fragmentation, but when these benefits were pointed out and also that WebSockets will soon work over HTTP/2, I retracted my objections: there are clear and concrete benefits of WebSockets over plain HTTP/2—though they’re admittedly mostly not quite ready yet.)
HTTP is cachable, can be proxied, inspected via curl and examined in Chrome's devtools. More importantly, typical app communication with the server has request-response shape. If you're communicating over a WebSockets connection, that doesn't change the natural shape of your queries. So you have to correlate requests and responses and multiplex over the connection - essentially reimplementing HTTP, but poorly!
If you have the choice, request-response communication is preferrable because it's always going to be simpler than bidirectional communication. If server push is an important part of your user experience, implement it as an addition to your HTTP routes and as a completely separate pathway (and consider using SSE instead of WebSockets).