net::err_empty_response is the worst kind of error: one that hands you no information about what just happened. You clicked a link, Chrome thought for half a second, and now you are looking at a string that looks like a serial number instead of a useful explanation. Most writeups treat the error like a riddle to decode. That framing is wrong. The error has no answer to decode. What it has is a single fact, and the rest is testing.
Here is the playbook I have built up over a few years of hitting this on my own browser and on small sites I help run. The first step is the only one that matters, and it takes thirty seconds.
The cellular test is the only test that needs to come first
Pull out your phone. Open the same site on cellular data. If the page loads there, the fault is somewhere on your side of the connection. If it does not load on cellular either, the fault is on the host’s side. That is the entire diagnosis for the question that matters, and you have answered it before touching a single setting.
I keep watching people skip this step. They clear their DNS, restart their router, disable every extension, and call hosting support, all without ever confirming which side of the connection actually broke. None of those steps are wrong. All of them are wasted effort if the fault is on the other side.
- Cellular data works for the test as long as it is a network that is not the one your machine is currently using.
- A friend’s hotspot works just as well as your own phone.
- The answer is binary. Local or upstream. Nothing else.
What -324 actually means
Chrome gave the error an internal log number, -324, and the meaning is narrow. The browser logged -324 because a TCP socket opened, then closed without sending any data. That is the entire failure mode the code covers. Status codes never arrive in this case because the server never got far enough to send one.
From inside Chrome, every cause looks identical. From outside, the causes are different, but you cannot see the outside from the browser window. The job is to find which layer broke, and the error text is not going to help you with that. The error is the browser admitting that something between it and the server stopped talking, and that is all the browser knows.
There are maybe half a dozen common causes. Worker exhaustion on the server side. A certificate that blocks the first response. A firewall rule that drops packets before they reach the web server. An extension that intercepts the connection on your end. That is the short list. None of them are exotic. All of them produce identical browser behavior.
- TCP open, TCP close, no payload. That is what -324 actually means.
- The browser is reporting silence, not a specific failure.
- There are half a dozen common causes, all invisible to the error text.
When the cellular test points upstream
Three things account for most of the cases I have seen when the fault is on the host side. The first is workers running out. Apache, Nginx, and IIS all keep a pool of background handlers that actually serve web requests. When that pool runs out, fresh connections arrive and immediately die with no reply. Look at the access and error logs for entries that show a connection opening with no corresponding handler entry. The fix is usually restarting the service or raising the worker count.
Certificate problems are the second common cause. An expired certificate, a self-signed certificate, or one missing Subject Alternative Names (the extra hostname entries a TLS certificate has to list to be valid for a domain) all stop the response before it leaves the server. The browser’s TLS handshake starts, and the server cuts the connection because the certificate fails validation.
Firewall rules are the third. Office networks, small business routers, and cloud security groups all carry rules that drop packets in both directions. The rule that fires here is usually one nobody remembers adding, often from a security audit years ago. Allowlist the domain and try again if you control the network. If you do not, you have done what you can from your end.
- Workers running out is a quick restart. Check the access log for connection-without-handler entries first.
- Certificate problems stop the response before headers, and look identical to worker exhaustion from the browser side.
- Firewall rules are the third common upstream cause, and the rule is usually one nobody remembers setting.
When the cellular test points at your machine
Two things account for most of the cases where the fault is local. The first is extensions. The second is local DNS. Both have fast tests.
Extensions are the single most common local cause. Anything sitting between Chrome and the network can decide a request looks suspicious and refuse to forward it. The browser writes -324. The extension gives you nothing to read. Open the page in Chrome’s Incognito mode, which disables most extensions in those windows. If the page loads there, an extension is the cause. If it does not, you have ruled out the most common client-side cause in about thirty seconds. Disable extensions one at a time to find the offender.
Local DNS is the second local cause worth checking. When Chrome can look up the domain but not reach the host, the same error appears. Flushing the local DNS cache clears stale entries that might be pointing at addresses that no longer work. On Windows that is ipconfig /flushdns. On macOS it is sudo dscacheutil -flushcache. Restart the browser after the flush, so the new resolver entries actually take effect. If your DNS provider is having a bad day, switching to a public resolver is a quick workaround. Cloudflare runs 1.1.1.1. Google runs 8.8.8.8.
- Incognito with extensions off rules out the most common local cause in about thirty seconds.
- Flushing DNS is the second test, and a public resolver is the workaround if your DNS provider is the problem.
When you control the server, the diagnosis is shorter
If you run the server, the logs are right there. Pull the access log for the affected domain at the time of the failure and read it. A request that reached the web server but never produced a handler entry points somewhere between the web server and the application. A request that never reached the application at all points at the web server itself or at the network before it.
A malformed .htaccess on Apache is a frequent culprit. Show hidden files in cPanel File Manager and look at the file directly. On WordPress sites, rename the plugins folder to plugins_disabled and reload. If the page comes back, a plugin was the cause. Turn them on one at a time and watch which one breaks things.
Shared hosting has ceilings that look identical to traffic spikes. Low PHP memory limits and a tight max_user_connections setting will both cut off replies as visitor numbers rise. Cloud setups hit the same ceiling through bad security group entries or missing NAT mappings, which route public traffic to private virtual machines. Both providers drop packets in their firewall logs and expose the offending rule through a dashboard. That visibility is the difference between a five-minute fix and an afternoon of guessing.
- The access log tells you which layer died. Read it before guessing.
- Renaming
plugins/toplugins_disabled/rules out a faulty WordPress plugin in seconds. - Shared hosting ceilings and cloud firewall rules look similar from the outside. The dashboards are how you tell them apart.
- Wrong security group entries and missing NAT mappings are the cloud version of the same wall.
When the tests run out, walk away
You have run the cellular test. You have flushed DNS. You have disabled extensions. You have read the server logs. The page is still blank. At this point the failure is most likely a real outage on the host side, and the honest move is to confirm it instead of guessing further.
Check the host’s status page if they have one. Try Downdetector or IsItDownRightNow for the domain to see if other users are reporting the same thing. Look at the host’s social media for incident reports, which often go up before the status page updates. If you are the site owner, contact support with the timestamp and the error code. That is what they need to pull the right logs.
Time spent guessing beyond this point is time spent being wrong. The fastest exit is to know when to stop.
- Status pages, Downdetector, and the host’s social feed are the three quickest sanity checks.
- A specific timestamp plus the error code is what support needs to find your log entry.
Trade-offs
Debugging this error costs time. The error itself does not name the cause, so the only honest path is to test each layer in turn. Plan on fifteen minutes of patient work the first time you meet this error on a new site. Add more time if you own the server but not the network between you and the host.
A second network is a free test, but it is not always available. A mobile hotspot works just as well as your own phone. The point is a network that is definitely not yours, not cellular specifically.
Owning the server shortens the diagnosis. Each hop between you and the host that someone else controls adds time and ambiguity. The cleanest setup is a server you fully admin and a network you fully admin. Anything else will cost you time on this error eventually.
What I would tell past me
If I could send a message back to the version of me that first saw net::err_empty_response and stared at it for an hour, I would say three things.
- Run the cellular test first. It answers the only question that matters in thirty seconds.
- Trust the connection shape, not the error text. TCP open, TCP close, no payload. That is the whole story.
- Walk away when the tests are exhausted. I have wasted afternoons because I did not want to admit the site was just down.
One last thing. This is one of the most misdiagnosed errors on the modern web because the error text tells you almost nothing. The fix is not clever. The fix is methodical. Run the cellular test. Try Incognito. Flush DNS. Read the server log if you have one. Stop when you have run out of layers to test.